Lab Objective

The following study will explore Bike Chattanooga’s usage patterns, weather patterns, and local context factors from March 01 to May 31, 2022 to develop a model that can potentially inform an effective rebalancing strategy.

Background

When Bike Chattanooga launched in 2012, it was the first city in the Southeastern US to offer a bikeshare program. Since then, over 600,000 trips have been taken on the Bike Chattanooga system - not a bad stat for a city of 180,000 residents. The city has 400 rental bicycles, including 55 E-Bikes, that are rented and may be returned at 42 locations around the municipality. With new grant money from the Tennessee DOT and private philanthropy, and demonstrated growth year over year, expanded e-bike stations and bike routes are in the pipeline. (source)

Urban bikeshare programs have evolved as a response to the increasing demand for sustainable and efficient urban transportation. Originating in the mid-20th century in Europe, early bikeshare systems gained popularity in the 21st century with the advent of technology and increased environmental consciousness. One of the pioneering systems was Lyon’s Vélo’v in 2005, setting the stage for global adoption. Subsequently, major cities worldwide introduced their own bikeshare programs. These initiatives typically operate through a network of strategically placed stations where users can rent bicycles for short trips. Users access bikes through mobile apps or membership cards, unlocking them at one station and returning them at another. The pay-per-ride or subscription-based model encourages short-distance commuting, contributing to reduced traffic congestion and environmental impact while promoting a healthier lifestyle.

The success of urban bikeshare programs hinges on their accessibility, ease of use, and integration with public transit systems. Rebalancing is a critical logistics hurdle for bikeshare program’s reliability and success. Since many cities experience bikeshare ‘rush hours’ it is often the case that bikes are not where riders need them to be. Rebalancing involves the redistribution of bicycles across stations to address spatial and temporal variations in user demand. Currently, many cities employ dedicated teams of rebalancing staff who manually transport bikes between stations based on real-time usage patterns and predictive models. However, the manual nature of this process poses logistical challenges, including high operational costs, inefficiencies, and delays in response to sudden demand fluctuations. The challenge lies in maintaining a delicate equilibrium in bike distribution to optimize user experience, making it imperative for bikeshare programs to explore more efficient and data-driven rebalancing strategies.

Methods

This study employs three primary datasets:

  1. Bike Chattanooga station and ridership data from Spring 2022
  2. weather data collected at Chattanooga Metropolitan Airport during those same dates and accessed via the riem package in R
  3. 2021 5-Y American Community Survey data for Hamilton County, TN.
  4. Open Streets Maps data about highway and waterway locations.

Data Wrangling

Wrangle the data to be operable with the lubridate R package.

dat <- st_read("/Users/LauraFrances_1/Library/CloudStorage/GoogleDrive-lauramuellersoppart@gmail.com/My Drive/Penn/Courses/MUSA508_PPA/PPA_Assignment5/BikeShare/Bike_Chattanooga_Trip_Data_Mar22_May22.csv")

dat$Start.Time <- gsub("/", "-", dat$Start.Time)
dat$Start.Time <- gsub(" AM$", " AM", dat$Start.Time)
dat$Start.Time <- gsub(" PM$", " PM", dat$Start.Time)
dat$End.Time <- gsub("/", "-", dat$End.Time)
dat$End.Time <- gsub(" AM$", " AM", dat$End.Time)
dat$End.Time <- gsub(" PM$", " PM", dat$End.Time)

# Convert to 24-hour clock
dat$Start.Time <- mdy_hms(dat$Start.Time)

# Format the result in 24-hour clock
dat$Start.Time <- format(dat$Start.Time, "%Y-%m-%d %H:%M:%S")

#Parse Out from Lat Long 
dat$from_longitude <- as.numeric(gsub("POINT \\((-?\\d+\\.\\d+) \\d+\\.\\d+\\)", "\\1", dat$Start.Location))
dat$from_latitude <- as.numeric(gsub("POINT \\(-?\\d+\\.\\d+ (-?\\d+\\.\\d+)\\)", "\\1", dat$Start.Location))
dat$to_longitude <- as.numeric(gsub("POINT \\((-?\\d+\\.\\d+) \\d+\\.\\d+\\)", "\\1", dat$End.Location))
dat$to_latitude <- as.numeric(gsub("POINT \\(-?\\d+\\.\\d+ (-?\\d+\\.\\d+)\\)", "\\1", dat$End.Location))

#Create time bins 
dat2 <- dat %>%
  mutate(interval60 = floor_date(ymd_hms(Start.Time), unit = "hour"), #downside is that if it's 7:59, itll be 7:00 
         interval15 = floor_date(ymd_hms(Start.Time), unit = "15 mins"),
         week = week(interval60),
         dotw = wday(interval60, label=TRUE))

Load in 2021 5Y American Community Survey Census Data for Hamilton County. We evaluate the following variables: Total population, median household income, median age, white vs non-white populations, percent public transit commuters, and median commute time.

chatCensus <- 
  get_acs(geography = "tract", 
          variables = c("B01003_001", "B19013_001", 
                        "B02001_002", "B08013_001",
                        "B08012_001", "B08301_001", 
                        "B08301_010", "B01002_001"), 
          year = 2021, 
          state = 47, 
          county= 065,
          geometry = TRUE, 
          output = "wide") %>%
  rename(Total_Pop =  B01003_001E,
         Med_Inc = B19013_001E,
         Med_Age = B01002_001E,
         White_Pop = B02001_002E,
         Travel_Time = B08013_001E,
         Num_Commuters = B08012_001E,
         Means_of_Transport = B08301_001E,
         Total_Public_Trans = B08301_010E) %>%
  select(Total_Pop, Med_Inc, White_Pop, Travel_Time,
         Means_of_Transport, Total_Public_Trans,
         Med_Age,
         GEOID, geometry) %>%
  mutate(Percent_White = White_Pop / Total_Pop,
         Mean_Commute_Time = Travel_Time / Total_Public_Trans,
         Percent_Taking_Public_Trans = Total_Public_Trans / Means_of_Transport)

#extract geometries 

city_limits <- st_read("/Users/LauraFrances_1/Library/CloudStorage/GoogleDrive-lauramuellersoppart@gmail.com/My Drive/Penn/Courses/MUSA508_PPA/PPA_Assignment5/BikeShare/Chattanooga City Limits/geo_export_48f449e6-f033-4fc7-afa3-be73bce74c1d.shp") %>% 
  st_transform(crs=st_crs(chatCensus)) 

city_limits <- st_make_valid(city_limits)
chatCensus <- st_make_valid(chatCensus)

# Perform the intersection
intersected_chatTracts <- st_intersection(chatCensus, city_limits)

# Extract geometries for the intersected tracts
chatTracts <- 
  intersected_chatTracts %>%
  as.data.frame() %>%
  distinct(GEOID, .keep_all = TRUE) %>%
  select(GEOID, geometry) %>% 
  st_sf

#add census tracts 
dat_census <- st_join(dat2 %>% 
          filter(is.na(from_longitude) == FALSE &
                   is.na(from_latitude) == FALSE &
                   is.na(to_latitude) == FALSE &
                   is.na(to_longitude) == FALSE) %>%
          st_as_sf(., coords = c("from_longitude", "from_latitude"), crs = 4326),
        chatTracts %>%
          st_transform(crs=4326),
        join=st_intersects,
              left = TRUE) %>%
  rename(Origin.Tract = GEOID) %>%
  mutate(from_longitude = unlist(map(geometry, 1)),
         from_latitude = unlist(map(geometry, 2)))%>%
  as.data.frame() %>%
  select(-geometry)%>%
  st_as_sf(., coords = c("to_longitude", "to_latitude"), crs = 4326) %>%
  st_join(., chatTracts %>%
            st_transform(crs=4326),
          join=st_intersects,
          left = TRUE) %>%
  rename(Destination.Tract = GEOID)  %>%
  mutate(to_longitude = unlist(map(geometry, 1)),
         to_latitude = unlist(map(geometry, 2)))%>%
  as.data.frame() %>%
  select(-geometry)

Load in weather data from Chattanooga Metropolitan Airport’s weather station for March 01 to May 31, 2022. Chattanooga has a distinct spring season where by mid-April precipitation falls and temperatures steadily rise.

weather.Panel <- 
  riem_measures(station = "KCHA", date_start = "2022-03-01", date_end = "2022-05-31") %>%
  dplyr::select(valid, tmpf, p01i, sknt)%>%
  replace(is.na(.), 0) %>%
    mutate(interval60 = ymd_h(substr(valid,1,13))) %>%
    mutate(week = week(interval60),
           dotw = wday(interval60, label=TRUE)) %>%
    group_by(interval60) %>%
    summarize(Temperature = max(tmpf),
              Precipitation = sum(p01i),
              Wind_Speed = max(sknt)) %>%
    mutate(Temperature = ifelse(Temperature == 0, 42, Temperature))

grid.arrange(
  ggplot(weather.Panel, aes(interval60,Precipitation)) + geom_line() + 
  labs(title="Percipitation", x="Hour", y="Perecipitation") + plotTheme,
  ggplot(weather.Panel, aes(interval60,Wind_Speed)) + geom_line() + 
    labs(title="Wind Speed", x="Hour", y="Wind Speed") + plotTheme,
  ggplot(weather.Panel, aes(interval60,Temperature)) + geom_line() + 
    labs(title="Temperature", x="Hour", y="Temperature") + plotTheme,
  top="Weather Data - Chattanooga KCHA - Spring 2022")

# # Create a trend line for weekly rides to add on top of weather 
# trend_line <- ggplot(dat_census %>%
#                        group_by(week) %>%
#                        tally(),
#                      aes(x = week, y = n)) +
#   geom_line(size = 0.2)

Exploratory Analysis

Bike Chattanooga has 42 stations that are concentrated in the Downtown and North Chattanooga neighborhoods.

highways <- opq("Chattanooga, TN") %>% 
  add_osm_feature(key = "highway", value = c("motorway", "motorway_link", "trunk", "primary")) %>% 
  osmdata_sf()

rivers <- opq("Chattanooga, TN") %>% 
  add_osm_feature(key = 'waterway', value = "river") %>%
  osmdata_sf()

ggplot() +
  geom_sf(data = chatTracts %>% st_transform(crs = 4326), fill = "darkgray", color = "gray90") +
  geom_sf(data = highways$osm_lines, color = "gray30", size = 0.3, alpha = 0.5) +
  geom_sf(data = rivers$osm_polygons, color = "lightblue", alpha = 0.5) +
  geom_point(data = dat2, aes(x = from_longitude, y = from_latitude), 
             color = "blue", alpha = 0.7, size = 0.3) +
  labs(title = "Distribution of Bikeshare Stations in Chattanooga",
       subtitle = "March - May 2022 | Dark gray lines are major motorways | Light gray lines are census tracts")+ 
  mapTheme

# #station by rides - tried to show station popularity
# ggplot()+
#   geom_sf(data = chatTracts %>% st_transform(crs = 4326), fill = "darkgray", color = "gray90") +
#   geom_point(data = dat_census %>%
#               group_by(Start.Station.ID) %>%
#               summarize(avg_weekly_rides = mean(n)),
#             aes(x = from_longitude, y = from_latitude, color = avg_weekly_rides, size = avg_weekly_rides),
#             alpha = 1) +
#   # scale_fill_gradient(low = "#D2FBD4", high = "#527D82", na.value = "transparent") +
#   scale_colour_viridis(direction = -1, discrete = FALSE, option = "G")+
#   ylim(min(dat_census$from_latitude), max(dat_census$from_latitude))+
#   xlim(min(dat_census$from_longitude), max(dat_census$from_longitude))+
#   labs(title="Avg Weekly Bike share trips by station",
#           subtitle = "March - May 2022 | Chattanooga, TN")+
#   mapTheme

Temporal Analysis

Examining our bike share data, we notice clear temporal trends: peaks during certain hours of the day and a longer-time decline in trips across the season. There is a spike of activity in early March during the Erlanger Chattanooga Marathon weekend. Generally, weekend days are more popular for riding Bike Chattanooga than weekday days.

ggplot(dat_census %>%
         group_by(interval60) %>%
         tally())+
  geom_line(aes(x = interval60, y = n), size=0.2)+
  labs(title="Bike share trips per hour",
       subtitle = "March - May 2022 | Chattanooga, TN",
       x="Date", 
       y="Number of trips")+
  plotTheme

Breaking these trends down to the, we notice peaks at certain times of day and certain days of the week. Overall ridership is higher on the weekend, and peaks a few hours earlier compared to the weekday’s after work rush. Generally, afternoon rush hours are associated with the highest ridership volumes during the week.

ggplot(dat_census %>% mutate(hour = hour(Start.Time)))+
     geom_freqpoly(aes(hour, color = dotw), binwidth = 1)+
    scale_color_manual(values = c("#D74E09", "#bdd7e7","#6baed6","#3182bd","#08519c", "blue", "#6E0E0A"), name = "Day") + 
  labs(title="Bike share trips, by day of the week",
      subtitle = "March - May 2022 | Chattanooga, TN",
       x="Hour", 
       y="Trip Counts")+
     plotTheme

ggplot(dat_census %>% 
         mutate(hour = hour(Start.Time),
                weekend = ifelse(dotw %in% c("Sun", "Sat"), "Weekend", "Weekday")))+
     geom_freqpoly(aes(hour, color = weekend), binwidth = 1)+
  scale_color_manual(values = c("#3182bd", "#D74E09")) +
  labs(title="Bike share trips - weekend vs weekday",
        subtitle = "March - May 2022 | Chattanooga, TN",
       x="Hour", 
       y="Trip Counts")+
     plotTheme

dat_census %>%
        mutate(time_of_day = case_when(hour(interval60) < 7 | hour(interval60) > 18 ~ "Overnight",
                                 hour(interval60) >= 7 & hour(interval60) < 10 ~ "AM Rush",
                                 hour(interval60) >= 10 & hour(interval60) < 15 ~ "Mid-Day",
                                 hour(interval60) >= 15 & hour(interval60) <= 18 ~ "PM Rush"))%>%
         group_by(interval60, Start.Station.Name, time_of_day) %>%
         tally()%>%
  group_by(Start.Station.Name, time_of_day)%>%
  summarize(mean_trips = mean(n))%>%
  ggplot()+
  geom_histogram(aes(mean_trips, fill=time_of_day), binwidth = 1)+
  scale_fill_manual(values = palette5, name="Time of Day") + 
  labs(title="Mean Number of Hourly Trips Per Station",
       subtitle = "March - May 2022 | Chattanooga, TN",
       x="Number of stations", 
       y="Trips Frequency")+
  facet_wrap(~time_of_day)+
  theme(legend.position = "none") +
  plotTheme

Spatial Analysis

Spatially, we observe that the highest ridership per station clusters in the Downtown and North Chattanooga areas, particularly in the mid-day and afternoon.

ggplot()+
  geom_sf(data = chatTracts %>% st_transform(crs = 4326), fill = "darkgray", color = "gray90") +
  geom_point(data = dat_census %>% 
            mutate(hour = hour(Start.Time),
                weekend = ifelse(dotw %in% c("Sun", "Sat"), "Weekend", "Weekday"),
                time_of_day = case_when(hour(interval60) < 7 | hour(interval60) > 18 ~ "Overnight",
                                 hour(interval60) >= 7 & hour(interval60) < 10 ~ "AM Rush",
                                 hour(interval60) >= 10 & hour(interval60) < 15 ~ "Mid-Day",
                                 hour(interval60) >= 15 & hour(interval60) <= 18 ~ "PM Rush"))%>%
              group_by(Start.Station.ID, from_latitude, from_longitude, weekend, time_of_day) %>%
              tally(),
            aes(x=from_longitude, y = from_latitude, color = n), 
            fill = "transparent", alpha = 1, size = 0.3)+
  # scale_fill_gradient(low = "#D2FBD4", high = "#527D82", na.value = "transparent") +
  scale_colour_viridis(direction = -1, discrete = FALSE, option = "G")+
  ylim(min(dat_census$from_latitude), max(dat_census$from_latitude))+
  xlim(min(dat_census$from_longitude), max(dat_census$from_longitude))+
  facet_grid(weekend ~ time_of_day)+
  labs(title="Bike share trips per hr by station",
          subtitle = "March - May 2022 | Chattanooga, TN")+
  mapTheme

#mapping demographics in chattanooga to better understand the city's dynamics 

#chattanooga tracts
intersected_chatCensus <- st_intersection(chatCensus, chatTracts)

plot1 <- ggplot() +
  geom_sf(data = chatTracts %>% st_transform(crs = 4326), fill = "darkgray", color = "gray90") +
  geom_sf(data = intersected_chatCensus, aes(fill = Total_Pop), color = "white", size = 0.05) +
  geom_sf(data = highways$osm_lines, color = "gray30", size = 0.3, alpha = 0.5) +
  geom_sf(data = rivers$osm_polygons, color = "lightblue", alpha = 0.5) +
  geom_point(data = dat2, aes(x = from_longitude, y = from_latitude), 
             color = "blue", alpha = 0.7, size = 0.3) +
  scale_fill_gradient(low = "#D2FBD4", high = "#527D82", na.value = "transparent") +
  labs(title = "Tracts by Total Population", subtitle = "2022 | Chattanooga, TN") +
  ylim(min(dat_census$from_latitude), max(dat_census$from_latitude))+
  xlim(min(dat_census$from_longitude), max(dat_census$from_longitude))+
  theme(legend.position = "bottom") +
  mapTheme

plot2 <- ggplot() +
  geom_sf(data = chatTracts %>% st_transform(crs = 4326), fill = "darkgray", color = "gray90") +
  geom_sf(data = intersected_chatCensus, aes(fill = Med_Inc), color = "white", size = 0.05) +
  geom_sf(data = highways$osm_lines, color = "gray30", size = 0.3, alpha = 0.5) +
  geom_sf(data = rivers$osm_polygons, color = "lightblue", alpha = 0.5) +
  geom_point(data = dat2, aes(x = from_longitude, y = from_latitude), 
             color = "blue", alpha = 0.7, size = 0.3) +
  scale_fill_gradient(low = "#D2FBD4", high = "#527D82", na.value = "transparent") +
  labs(title = "Tracts by Median Income", subtitle = "2022 | Chattanooga, TN") +
  ylim(min(dat_census$from_latitude), max(dat_census$from_latitude))+
  xlim(min(dat_census$from_longitude), max(dat_census$from_longitude))+
  theme(legend.position = "bottom") +
  mapTheme


plot3 <- ggplot() +
  geom_sf(data = chatTracts %>% st_transform(crs = 4326), fill = "darkgray", color = "gray90") +
  geom_sf(data = intersected_chatCensus %>% mutate(Percent_Non_White = 1 - Percent_White), 
          aes(fill = Percent_Non_White), color = "white", size = 0.05) +
  geom_sf(data = highways$osm_lines, color = "gray30", size = 0.3, alpha = 0.5) +
  geom_sf(data = rivers$osm_polygons, color = "lightblue", alpha = 0.5) +
  geom_point(data = dat2, aes(x = from_longitude, y = from_latitude), 
             color = "blue", alpha = 0.7, size = 0.3) +
  scale_fill_gradient(low = "#D2FBD4", high = "#527D82", na.value = "transparent") +
  labs(title = "Tracts by Percent Non-White", subtitle = "2022 | Chattanooga, TN") +
  ylim(min(dat_census$from_latitude), max(dat_census$from_latitude))+
  xlim(min(dat_census$from_longitude), max(dat_census$from_longitude))+
  theme(legend.position = "bottom") +
  mapTheme

# # # install.packages("patchwork")
# library(patchwork)
# plot1 + plot2 + plot3

Feature Engineering

To build our model, we conduct feature engineering, exploratory data analysis, and correlation analysis. The primary engineered feature are temporal lags which represent the number of trips in the previous hours. We find that the lags are strongly correlated with our dependent variable, Trip_Count, which provides strong insight into how to re-balance the Bike Chattanooga fleet.

Time-Space Panel

length(unique(dat_census$interval60)) * length(unique(dat_census$Start.Station.ID)) #69720 combinations

study.panel <- 
  expand.grid(interval60=unique(dat_census$interval60), #going to populate combinations 
              Start.Station.ID = unique(dat_census$Start.Station.ID)) %>% #summarize by hour and station
  left_join(., dat_census %>% #joins by station_id since its the only unique variable 
              select(Start.Station.ID, Start.Station.Name, Origin.Tract, from_longitude, from_latitude )%>%
              distinct() %>%
              group_by(Start.Station.ID) %>%
              slice(1))

ride.panel <- 
  dat_census %>%
  mutate(Trip_Counter = 1) %>%
  right_join(study.panel) %>% 
  group_by(interval60, Start.Station.ID, Start.Station.Name, Origin.Tract, from_longitude, from_latitude) %>%
  summarize(Trip_Count = sum(Trip_Counter, na.rm=T)) %>%
  left_join(weather.Panel) %>%
  ungroup() %>%
  filter(is.na(Start.Station.ID) == FALSE) %>%
  mutate(week = week(interval60),
         dotw = wday(interval60, label = TRUE)) %>%
  filter(is.na(Origin.Tract) == FALSE)

ride.panel <- 
  left_join(ride.panel, chatCensus %>%
              as.data.frame() %>%
              select(-geometry), by = c("Origin.Tract" = "GEOID"))

## Create Time Logs

#install.packages("spatialreg")
library(spatialreg)

ride.panel <- 
  ride.panel %>% 
  arrange(Start.Station.ID, interval60) %>%  
  mutate(lagHour = dplyr::lag(Trip_Count,1), #from an hour ago
         lag2Hours = dplyr::lag(Trip_Count,2), #from two hours ago
         lag3Hours = dplyr::lag(Trip_Count,3), #from three hours ago, etc. 
         lag4Hours = dplyr::lag(Trip_Count,4),
         lag12Hours = dplyr::lag(Trip_Count,12),
         lag1day = dplyr::lag(Trip_Count,24),
         holiday = ifelse(yday(interval60) == 148,1,0)) %>% #Memorial day - but when I change to Marathon weekend, Day 65, it breaks - not sure why
   mutate(day = yday(interval60)) %>%
   mutate(holidayLag = case_when(dplyr::lag(holiday, 1) == 1 ~ "PlusOneDay",
                                 dplyr::lag(holiday, 2) == 1 ~ "PlusTwoDays",
                                 dplyr::lag(holiday, 3) == 1 ~ "PlusThreeDays",
                                 dplyr::lead(holiday, 1) == 1 ~ "MinusOneDay",
                                 dplyr::lead(holiday, 2) == 1 ~ "MinusTwoDays",
                                 dplyr::lead(holiday, 3) == 1 ~ "MinusThreeDays"),
         holidayLag = ifelse(is.na(holidayLag) == TRUE, 0, holidayLag)) 


#look at the correlation between the lags
as.data.frame(ride.panel) %>%
    group_by(interval60) %>% 
    summarise_at(vars(starts_with("lag"), "Trip_Count"), mean, na.rm = TRUE) %>%
    gather(Variable, Value, -interval60, -Trip_Count) %>%
    mutate(Variable = factor(Variable, levels=c("lagHour","lag2Hours","lag3Hours","lag4Hours",
                                                "lag12Hours","lag1day")))%>%
    group_by(Variable) %>%  
    summarize(correlation = round(cor(Value, Trip_Count),2)) %>%
    kbl(caption = "Correlations between Engineered Features and Dependent Variable") %>%
    kable_paper(bootstrap_options = "striped", full_width = TRUE)

To provide a more intuitive visual representation of the bike share trips, we plot an animated map with temporal and spatial context. Additionally, we observe that stations are currently located in wealthier neighborhoods rather than the most populous areas.

stations<-
  dat2 %>% 
  dplyr::select(Start.Station.ID, from_longitude, from_latitude) %>%
  group_by(Start.Station.ID) %>%
  distinct()%>%
  filter(is.na(from_longitude) == FALSE &
           is.na(from_latitude) == FALSE) %>%
  st_as_sf(., coords = c("from_longitude", "from_latitude"), crs = 4326)


one_day <-
  dat_census %>%
  filter(week == 14 & dotw == "Thu")

one_day.panel <-
  expand.grid(
    interval60 = unique(one_day$interval60),
    Origin.Tract = unique(ride.panel$Origin.Tract))


animation_data <-
  mutate(one_day, Trip_Counter = 1) %>%
  right_join(one_day) %>% 
  group_by(interval60, Start.Station.ID) %>%
  summarise(Trip_Count = sum(Trip_Counter, na.rm=T)) %>% 
  ungroup() %>% 
  right_join(stations, by=c("Start.Station.ID" = "Start.Station.ID")) %>% 
  st_sf() %>%
  mutate(Trips = case_when(Trip_Count == 0 ~ "0 trips",
                           Trip_Count == 1 ~ "1 trips",
                           Trip_Count == 2 ~ "2 trips",
                           Trip_Count > 2 & Trip_Count <= 4 ~ "3-4 trips",
                           Trip_Count > 4 ~ "5+ trips")) %>%
  mutate(Trips  = fct_relevel(Trips, "0 trips","1 trips","2 trips",
                              "3-4 trips","5+ trips"))


bike_animation <-
  ggplot() +
  geom_sf(data = chatCensus %>%
            st_transform(crs=4326))+
  geom_sf(data = chatCensus, aes(fill = Med_Inc), color = "white", size = 0.05) +
  geom_sf(data = animation_data, 
             aes(color = Trips), size = 2) +
  scale_color_manual(values = palette4) +
  scale_fill_gradient(low = "gray90", high = "gray30", na.value = "white") +  # Change the color palette
 ylim(min(dat_census$from_latitude), max(dat_census$from_latitude))+
 xlim(min(dat_census$from_longitude), max(dat_census$from_longitude))+
  labs(title = "Bike Trips by Start Stations for the first week of April, 2022",
       subtitle = "60-minute intervals: {current_frame}") +
  transition_manual(interval60) +
  mapTheme

animate(bike_animation, duration=10, renderer = gifski_renderer())

Spatiotemporal Prediction

To conduct regression analysis and prediction on bike ride data, the data is split into training (ride.Train) and testing (ride.Test) sets based on the week variable. Several linear regression models (reg1 to reg6) are fitted using the ride.Train data and different combinations of predictor variables, including hour of the day, day of the week, temperature, precipitation, lags of trip counts, holiday information, and demographic variables.

When comparing Model 3 (Start.Station.Name + hour(interval60) + dotw + Temperature + Precipitation) and Model 6 (Model 3 + lagHour + lag2Hours +lag3Hours +lag12Hours + lag1day + holidayLag + holiday + Total_Pop + Med_Inc + Percent_Taking_Public_Trans), Model 6 has a higher R² (0.227) compared to Model 3 (0.171) which suggests that Model 6 explains a larger proportion of the variance in the dependent variable compared to Model 3. Additionally, Model 6 has a slightly lower residual standard error (1.201) compared to Model 3 (1.243) which indicates a better fit of the model to the data. Lastly, while both models have a significant F statistic, Model 6 (F = 63.822) has a higher value compared to Model 3 (F = 55.888).

#splits data about in half 
ride.Train <- filter(ride.panel, week >= 20)
ride.Test <- filter(ride.panel, week < 20)

reg1 <- 
  lm(Trip_Count ~  hour(interval60) + dotw + Temperature,  data=ride.Train)

reg2 <- 
  lm(Trip_Count ~  Start.Station.Name + dotw + Temperature,  data=ride.Train)

reg3 <- 
  lm(Trip_Count ~  Start.Station.Name + hour(interval60) + dotw + Temperature + Precipitation, 
     data=ride.Train)

reg4 <- 
  lm(Trip_Count ~  Start.Station.Name +  hour(interval60) + dotw + Temperature + Precipitation +
                   lagHour + lag2Hours +lag3Hours + lag12Hours + lag1day, 
     data=ride.Train)

reg5 <- 
  lm(Trip_Count ~  Start.Station.Name + hour(interval60) + dotw + Temperature + Precipitation +
                   lagHour + lag2Hours +lag3Hours +lag12Hours + lag1day + holidayLag + holiday, 
     data=ride.Train)

reg6 <-
  lm(Trip_Count ~  Start.Station.Name + hour(interval60) + dotw + Temperature + Precipitation +
                   lagHour + lag2Hours +lag3Hours +lag12Hours + lag1day + holidayLag + holiday + 
                   Total_Pop + Med_Inc + Percent_Taking_Public_Trans, 
     data=ride.Train)
library(stargazer)
stargazer(reg3, reg6, type="text") 

We defined a function to predict trip counts for a given model and test dataset. For each model (reg1 to reg6), predictions are generated along with observed values, are gathered into a long format, and additional metrics like Absolute Error (AE), Mean Absolute Error (MAE), and standard deviation of AE.

#Predict for test data 
ride.Test.weekNest <- 
  ride.Test %>%
  nest(-week) 

model_pred <- function(dat, fit){
   pred <- predict(fit, newdata = dat)} #predict for fit using the dat dataframe 

week_predictions <- 
  ride.Test.weekNest %>% 
    mutate(ATime_FE = map(.x = data, fit = reg1, .f = model_pred), #mapping the function for different models d
           BSpace_FE = map(.x = data, fit = reg2, .f = model_pred),
           CTime_Space_FE = map(.x = data, fit = reg3, .f = model_pred),
           DTime_Space_FE_timeLags = map(.x = data, fit = reg4, .f = model_pred),
           ETime_Space_FE_timeLags_holidayLags = map(.x = data, fit = reg5, .f = model_pred),
           FTime_Space_FE_timeLags_holidayLags_demo = map(.x = data, fit = reg6, .f = model_pred)) %>% 
    gather(Regression, Prediction, -data, -week) %>%
    mutate(Observed = map(data, pull, Trip_Count),
           Absolute_Error = map2(Observed, Prediction,  ~ abs(.x - .y)),
           MAE = map_dbl(Absolute_Error, mean, na.rm = TRUE),
           sd_AE = map_dbl(Absolute_Error, sd, na.rm = TRUE))

Discussion

Examine Error Metrics for Accuracy

The plot illustrates the Mean Absolute Errors (MAE) for different model specifications across weeks. Notably, Models 4, 5, and 6 include time lags as a feature and consistently display lower MAE. While Model D has a slightly lower MAE, the enhanced statistical performance of Model F may justify its marginally higher MAE.

week_predictions %>%
  dplyr::select(week, Regression, MAE) %>%
  gather(Variable, MAE, -Regression, -week) %>%
  ggplot(aes(week, MAE)) + 
    geom_bar(aes(fill = Regression), position = "dodge", stat="identity") +
    scale_fill_manual(values = palette6) +
    labs(title = "Mean Absolute Errors by model specification and week") +
  plotTheme

The plot illustrates the predicted and observed time series of bike share trips for different model specifications across multiple weeks. The discrepancy between observed and predicted values, particularly around peak times, may arise from the inherent complexity of real-world bike share trip patterns. Factors such as external events, unforeseen fluctuations in demand, or unaccounted variables in the models may contribute to the differences. Generally, this comparative analysis shows how well each model aligns with the observed data, providing insights into the predictive performance of different model specifications.

week_predictions %>% 
    mutate(interval60 = map(data, pull, interval60),
           Start.Station.ID = map(data, pull, Start.Station.ID)) %>%
    dplyr::select(interval60, Start.Station.ID, Observed, Prediction, Regression) %>%
    unnest() %>%
    gather(Variable, Value, -Regression, -interval60, -Start.Station.ID) %>%
    group_by(Regression, Variable, interval60) %>%
    summarize(Value = sum(Value)) %>%
    ggplot(aes(interval60, Value, colour=Variable)) + 
      geom_line(size = 1.1) + 
      facet_wrap(~Regression, ncol=1) +
      labs(title = "Predicted/Observed bike share time series", subtitle = "March - May 2022 | Chattanooga | A test set of 2 weeks",  x = "Hour", y= "Station Trips") +
      plotTheme

Spatially, we observe that there is higher MAE at stations proximate to the Tennessee River.

Alt text

week_predictions %>% 
    mutate(interval60 = map(data, pull, interval60),
           Start.Station.ID = map(data, pull, Start.Station.ID), 
           from_latitude = map(data, pull, from_latitude), 
           from_longitude = map(data, pull, from_longitude)) %>%
    select(interval60, Start.Station.ID, from_longitude, from_latitude, Observed, Prediction, Regression) %>%
    unnest() %>%
  filter(Regression == "FTime_Space_FE_timeLags_holidayLags_demo") %>%
  group_by(Start.Station.ID, from_longitude, from_latitude) %>%
  summarize(MAE = mean(abs(Observed-Prediction), na.rm = TRUE))%>%
ggplot(.)+
  geom_sf(data = chatCensus, color = "grey", fill = "transparent")+
  geom_sf(data = highways$osm_lines, color = "gray30", size = 0.3, alpha = 0.5) +
  geom_sf(data = rivers$osm_polygons, color = "lightblue", alpha = 0.5) +
  geom_point(aes(x = from_longitude, y = from_latitude, color = MAE), 
             fill = "transparent", alpha = 0.8)+
  scale_colour_viridis(direction = -1,
  discrete = FALSE, option = "D")+
  ylim(min(dat_census$from_latitude), max(dat_census$from_latitude))+
  xlim(min(dat_census$from_longitude), max(dat_census$from_longitude))+
  labs(title="Mean Abs Error, Test Set, Model F", 
       subtitle = "Time_Space_FE_timeLags_holidayLags_demo")+
  mapTheme


# install.packages("mapview")
# library(mapview)
# 
# # Assuming you have an sf object for chatCensus
# chatCensus_sf <- st_as_sf(chatCensus)
# 
# # Your existing code
# result <- week_predictions %>% 
#   mutate(interval60 = map(data, pull, interval60),
#          Start.Station.ID = map(data, pull, Start.Station.ID), 
#          from_latitude = map(data, pull, from_latitude), 
#          from_longitude = map(data, pull, from_longitude)) %>%
#   select(interval60, Start.Station.ID, from_longitude, from_latitude, Observed, Prediction, Regression) %>%
#   unnest() %>%
#   filter(Regression == "FTime_Space_FE_timeLags_holidayLags_demo") %>%
#   group_by(Start.Station.ID, from_longitude, from_latitude) %>%
#   summarize(MAE = mean(abs(Observed - Prediction), na.rm = TRUE))
# 
# # Creating an sf object for the results
# result_sf <- st_as_sf(result, coords = c("from_longitude", "from_latitude"))
# 
# # Plotting with mapview
# mapview(chatCensus_sf) +
#   mapview(highways$osm_lines, col.regions = "gray30", alpha.regions = 0.5) +
#   mapview(result_sf, zcol = "MAE", alpha = 0.8, legend = TRUE)

Space-Time Error Evaluation

The scatterplots depict the relationship between observed and predicted bike share trips, with each point representing a specific time period and station. While the plots show a generally positive correlation, the models undercount the number of predicted bike trips.

week_predictions %>% 
    mutate(interval60 = map(data, pull, interval60),
           Start.Station.ID = map(data, pull, Start.Station.ID), 
           from_latitude = map(data, pull, from_latitude), 
           from_longitude = map(data, pull, from_longitude),
           dotw = map(data, pull, dotw)) %>%
    select(interval60, Start.Station.ID, from_longitude, 
           from_latitude, Observed, Prediction, Regression,
           dotw) %>%
    unnest() %>%
  filter(Regression == "ETime_Space_FE_timeLags_holidayLags")%>%
  mutate(weekend = ifelse(dotw %in% c("Sun", "Sat"), "Weekend", "Weekday"),
         time_of_day = case_when(hour(interval60) < 7 | hour(interval60) > 18 ~ "Overnight",
                                 hour(interval60) >= 7 & hour(interval60) < 10 ~ "AM Rush",
                                 hour(interval60) >= 10 & hour(interval60) < 15 ~ "Mid-Day",
                                 hour(interval60) >= 15 & hour(interval60) <= 18 ~ "PM Rush"))%>%
  ggplot()+
  geom_point(aes(x= Observed, y = Prediction))+
    geom_smooth(aes(x= Observed, y= Prediction), method = "lm", se = FALSE, color = "blue")+
    geom_abline(slope = 1, intercept = 0)+
  facet_grid(time_of_day~weekend)+
  labs(title="Observed vs Predicted",
       x="Observed trips", 
       y="Predicted trips")+
  plotTheme

When comparing the the relationship between Mean Absolute Error (MAE) and three socio-economic variables (Percent_Taking_Public_Trans, Med_Inc, Percent_White) during the “AM Rush” versus the “PM Rush”, the “AM Rush” suggests a stronger, negative relationship bewteen errors and those who take public transportation to work.

week_predictions %>% 
    mutate(interval60 = map(data, pull, interval60),
           Start.Station.ID = map(data, pull, Start.Station.ID), 
           from_latitude = map(data, pull, from_latitude), 
           from_longitude = map(data, pull, from_longitude),
           dotw = map(data, pull, dotw),
           Percent_Taking_Public_Trans = map(data, pull, Percent_Taking_Public_Trans),
           Med_Inc = map(data, pull, Med_Inc),
           Percent_White = map(data, pull, Percent_White)) %>%
    select(interval60, Start.Station.ID, from_longitude, 
           from_latitude, Observed, Prediction, Regression,
           dotw, Percent_Taking_Public_Trans, Med_Inc, Percent_White) %>%
    unnest() %>%
  filter(Regression == "ETime_Space_FE_timeLags_holidayLags")%>%
  mutate(weekend = ifelse(dotw %in% c("Sun", "Sat"), "Weekend", "Weekday"),
         time_of_day = case_when(hour(interval60) < 7 | hour(interval60) > 18 ~ "Overnight",
                                 hour(interval60) >= 7 & hour(interval60) < 10 ~ "AM Rush",
                                 hour(interval60) >= 10 & hour(interval60) < 15 ~ "Mid-Day",
                                 hour(interval60) >= 15 & hour(interval60) <= 18 ~ "PM Rush")) %>%
  filter(time_of_day == "AM Rush") %>%
  group_by(Start.Station.ID, Percent_Taking_Public_Trans, Med_Inc, Percent_White) %>%
  summarize(MAE = mean(abs(Observed-Prediction), na.rm = TRUE))%>%
  gather(-Start.Station.ID, -MAE, key = "variable", value = "value")%>%
  ggplot(.)+
  #geom_sf(data = chatCensus, color = "grey", fill = "transparent")+
  geom_point(aes(x = value, y = MAE), alpha = 0.4)+
  geom_smooth(aes(x = value, y = MAE), method = "lm", se= FALSE)+
  facet_wrap(~variable, scales = "free")+
  labs(title="Errors as a function of socio-economic variables - AM RUSH",
       y="Mean Absolute Error (Trips)")+
  plotTheme

week_predictions %>% 
    mutate(interval60 = map(data, pull, interval60),
           Start.Station.ID = map(data, pull, Start.Station.ID), 
           from_latitude = map(data, pull, from_latitude), 
           from_longitude = map(data, pull, from_longitude),
           dotw = map(data, pull, dotw),
           Percent_Taking_Public_Trans = map(data, pull, Percent_Taking_Public_Trans),
           Med_Inc = map(data, pull, Med_Inc),
           Percent_White = map(data, pull, Percent_White)) %>%
    select(interval60, Start.Station.ID, from_longitude, 
           from_latitude, Observed, Prediction, Regression,
           dotw, Percent_Taking_Public_Trans, Med_Inc, Percent_White) %>%
    unnest() %>%
  filter(Regression == "ETime_Space_FE_timeLags_holidayLags")%>%
  mutate(weekend = ifelse(dotw %in% c("Sun", "Sat"), "Weekend", "Weekday"),
         time_of_day = case_when(hour(interval60) < 7 | hour(interval60) > 18 ~ "Overnight",
                                 hour(interval60) >= 7 & hour(interval60) < 10 ~ "AM Rush",
                                 hour(interval60) >= 10 & hour(interval60) < 15 ~ "Mid-Day",
                                 hour(interval60) >= 15 & hour(interval60) <= 18 ~ "PM Rush")) %>%
  filter(time_of_day == "PM Rush") %>%
  group_by(Start.Station.ID, Percent_Taking_Public_Trans, Med_Inc, Percent_White) %>%
  summarize(MAE = mean(abs(Observed-Prediction), na.rm = TRUE))%>%
  gather(-Start.Station.ID, -MAE, key = "variable", value = "value")%>%
  ggplot(.)+
  #geom_sf(data = chatCensus, color = "grey", fill = "transparent")+
  geom_point(aes(x = value, y = MAE), alpha = 0.4)+
  geom_smooth(aes(x = value, y = MAE), method = "lm", se= FALSE)+
  facet_wrap(~variable, scales = "free")+
  labs(title="Errors as a function of socio-economic variables - PM RUSH",
       y="Mean Absolute Error (Trips)")+
  plotTheme

Results

Cross-validation is performed across time, rather than space, to assess the performance and generalizability of a predictive model. The results of the model indicate a Mean Absolute Error (MAE) of approximately 0.646 with a standard deviation of around 0.037. A lower MAE is generally desirable, indicating better predictive performance. The standard deviation of the MAE gives an idea of the variability of prediction errors across different folds of the cross-validation process.

control <- trainControl(method="cv", number=100)

set.seed(42)

model_cv <- train(Trip_Count ~ Start.Station.ID + hour(interval60) + dotw + Temperature + Precipitation +
                   lagHour + lag2Hours +lag3Hours +lag12Hours + lag1day + holidayLag + holiday + 
                   Total_Pop + Med_Inc + Percent_Taking_Public_Trans, 
                  data=na.omit(ride.panel),
                  method="lm",
                  trControl=control)

model_cv$resample %>% 
  summarise(MAE = mean(model_cv$resample[,3]),
            sd(model_cv$resample[,3])
) %>%
  kbl(col.name=c('Mean Absolute Error','Standard Deviation of MAE')) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"))
Mean Absolute Error Standard Deviation of MAE
0.6456238 0.0367577

Enhancing the model’s performance relies on two main factors: refining feature engineering and increasing computational resources. Incorporating binary variables to account for factors like rush hours may improve accuracy. However, the most significant improvement would likely come from expanding the dataset, enabling the model to discern nuanced patterns, such as variations in commuting behavior on different holidays, such as Marathon Sunday.Additionally, transitioning from ordinary least squares (OLS) regression to models explicitly addressing spatial dependencies, such as spatial lag regression or geographically-weighted regression, particularly around the city’s highways and waterways. Ultimately, maximizing the dataset size would likely prove beneficial. Training the model on a more extensive historical dataset, encompassing the approximately 11 years of available Bike Chattanooga data, would likely yield substantial improvements in accuracy and generalizability.

Conclusion

Our model offers valuable insights for optimizing Bike-Chattanooga’s rebalancing efforts. Its overall accuracy surpasses a naive approach that reacts to emerging demand. However, challenges persist, particularly in handling the non-random spatial distribution of errors around the Tennessee River. Notably, the model struggles to generalize well in high-demand areas, where the consequences of underprediction are most pronounced. While the model remains a robust predictive tool, there are notable opportunities for improvement, especially in enhancing its treatment of spatial fixed effects

LS0tCnRpdGxlOiAiU3BhY2UtVGltZSBQcmVkaWN0aW9uIG9mIEJpa2UgU2hhcmUgRGVtYW5kIC0gQ2hhdHRhbm9vZ2EsIFROIgphdXRob3I6ICJMYXVyYSBGcmFuY2VzLCBVbml2ZXJzaXR5IG9mIFBlbm5zeWx2YW5pYSIKZGF0ZTogIk5vdmVtYmVyIDEyLCAyMDIzIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiAiaGlkZSIKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCgojIGluc3RhbGwucGFja2FnZXMoImx1YnJpZGF0ZSIpCiMgaW5zdGFsbC5wYWNrYWdlcygiZHlwbHIiKQojIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpCiMgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpCiMgaW5zdGFsbC5wYWNrYWdlcygic2YiKQojIGluc3RhbGwucGFja2FnZXMoInRpZHlyIikKIyBpbnN0YWxsLnBhY2thZ2VzKCJ0aWdyaXMiKQojIGluc3RhbGwucGFja2FnZXMoInRpZHljZW5zdXMiKQojIGluc3RhbGwucGFja2FnZXMoImdyaWRFeHRyYSIpCiMgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQojIGluc3RhbGwucGFja2FnZXMoImthYmxlRXh0cmEiKQojIGluc3RhbGwucGFja2FnZXMoIlJTb2NyYXRhIikKIyBpbnN0YWxsLnBhY2thZ2VzKCJ2aXJpZGlzIikKIyBpbnN0YWxsLnBhY2thZ2VzKCJ2aXJpZGlzTGl0ZSIpCiMgaW5zdGFsbC5wYWNrYWdlcygicmllbSIpCiNpbnN0YWxsLnBhY2thZ2VzKCJvc21kYXRhIikKI2luc3RhbGwucGFja2FnZXMoImdnYW5pbWF0ZSIpCiNpbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpCgpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShzZikKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkodGlncmlzKQpsaWJyYXJ5KHRpZHljZW5zdXMpCmxpYnJhcnkodmlyaWRpcykKbGlicmFyeShyaWVtKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShrbml0cikKbGlicmFyeShrYWJsZUV4dHJhKQpsaWJyYXJ5KFJTb2NyYXRhKQpsaWJyYXJ5KG9zbWRhdGEpCmxpYnJhcnkoZ2dhbmltYXRlKQpsaWJyYXJ5KGNhcmV0KQoKb3B0aW9ucyhzY2lwZW49OTk5KSAjbm8gc2NpZW50aWZpYyBub3RhdGlvbgpvcHRpb25zKHRpZ3Jpc19jbGFzcyA9ICJzZiIpICN3aGVuZXZlciB3ZSBnZXQgdGlncmlzLCB3ZSB3YW50IHNmIApvcHRpb25zKHRpZ3Jpc191c2VfY2FjaGUgPSBUUlVFKSAKCmNlbnN1c19hcGlfa2V5KCJjNDlhOTE0NWE1Y2FkNjAwYThiNjM5M2QzMWRiZWQ4NjJhMDIxMWExIiwgb3ZlcndyaXRlID0gVFJVRSwgaW5zdGFsbCA9IFRSVUUpCgpwbG90VGhlbWUgPC0gdGhlbWUoCiAgcGxvdC50aXRsZSA9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwKICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLAogIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICMgU2V0IHRoZSBlbnRpcmUgY2hhcnQgcmVnaW9uIHRvIGJsYW5rCiAgcGFuZWwuYmFja2dyb3VuZD1lbGVtZW50X2JsYW5rKCksCiAgcGxvdC5iYWNrZ3JvdW5kPWVsZW1lbnRfYmxhbmsoKSwKICAjcGFuZWwuYm9yZGVyPWVsZW1lbnRfcmVjdChjb2xvdXI9IiNGMEYwRjAiKSwKICAjIEZvcm1hdCB0aGUgZ3JpZAogIHBhbmVsLmdyaWQubWFqb3I9ZWxlbWVudF9saW5lKGNvbG91cj0iI0QwRDBEMCIsc2l6ZT0uMiksCiAgYXhpcy50aWNrcz1lbGVtZW50X2JsYW5rKCkpCgptYXBUaGVtZSA8LSB0aGVtZShwbG90LnRpdGxlID1lbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICAgICAgICAgICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KHNpemUgPSA2KSwKICAgICAgICAgICAgICAgICAgYXhpcy5saW5lPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgcGFuZWwuYm9yZGVyPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgcGFuZWwuZ3JpZC5tYWpvcj1lbGVtZW50X2xpbmUoY29sb3VyID0gJ3RyYW5zcGFyZW50JyksCiAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWlub3I9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gInZlcnRpY2FsIiwgCiAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsCiAgICAgICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDEsIDEsIDEsIDEsICdjbScpLAogICAgICAgICAgICAgICAgICBsZWdlbmQua2V5LmhlaWdodCA9IHVuaXQoMSwgImNtIiksIGxlZ2VuZC5rZXkud2lkdGggPSB1bml0KDAuMiwgImNtIikpCgpwYWxldHRlNiA8LSBjKCIjZWZmM2ZmIiwiI2JkZDdlNyIsIiM2YmFlZDYiLCIjMzE4MmJkIiwiIzA4NTE5YyIsICJibHVlIikKcGFsZXR0ZTUgPC0gYygiI2VmZjNmZiIsIiNiZGQ3ZTciLCIjNmJhZWQ2IiwiIzMxODJiZCIsIiMwODUxOWMiKQpwYWxldHRlNCA8LSBjKCIjRDJGQkQ0IiwiIzkyQkNBQiIsIiM1MjdEODIiLCIjMTIzRjVBIikKcGFsZXR0ZTIgPC0gYygiIzZiYWVkNiIsIiMwODUxOWMiKQoKYGBgCgojICoqTGFiIE9iamVjdGl2ZSoqCgpUaGUgZm9sbG93aW5nIHN0dWR5IHdpbGwgZXhwbG9yZSBCaWtlIENoYXR0YW5vb2dhJ3MgdXNhZ2UgcGF0dGVybnMsIHdlYXRoZXIgcGF0dGVybnMsIGFuZCBsb2NhbCBjb250ZXh0IGZhY3RvcnMgZnJvbSBNYXJjaCAwMSB0byBNYXkgMzEsIDIwMjIgdG8gZGV2ZWxvcCBhIG1vZGVsIHRoYXQgY2FuIHBvdGVudGlhbGx5IGluZm9ybSBhbiBlZmZlY3RpdmUgcmViYWxhbmNpbmcgc3RyYXRlZ3kuIAoKIyAqKkJhY2tncm91bmQqKgoKV2hlbiBCaWtlIENoYXR0YW5vb2dhIGxhdW5jaGVkIGluIDIwMTIsIGl0IHdhcyB0aGUgZmlyc3QgY2l0eSBpbiB0aGUgU291dGhlYXN0ZXJuIFVTIHRvIG9mZmVyIGEgYmlrZXNoYXJlIHByb2dyYW0uIFNpbmNlIHRoZW4sIG92ZXIgNjAwLDAwMCB0cmlwcyBoYXZlIGJlZW4gdGFrZW4gb24gdGhlIEJpa2UgQ2hhdHRhbm9vZ2Egc3lzdGVtIC0gbm90IGEgYmFkIHN0YXQgZm9yIGEgY2l0eSBvZiAxODAsMDAwIHJlc2lkZW50cy4gVGhlIGNpdHkgaGFzIDQwMCByZW50YWwgYmljeWNsZXMsIGluY2x1ZGluZyA1NSBFLUJpa2VzLCB0aGF0IGFyZSByZW50ZWQgYW5kIG1heSBiZSByZXR1cm5lZCBhdCA0MiBsb2NhdGlvbnMgYXJvdW5kIHRoZSBtdW5pY2lwYWxpdHkuIFdpdGggbmV3IGdyYW50IG1vbmV5IGZyb20gdGhlIFRlbm5lc3NlZSBET1QgYW5kIHByaXZhdGUgcGhpbGFudGhyb3B5LCBhbmQgZGVtb25zdHJhdGVkIGdyb3d0aCB5ZWFyIG92ZXIgeWVhciwgZXhwYW5kZWQgZS1iaWtlIHN0YXRpb25zIGFuZCBiaWtlIHJvdXRlcyBhcmUgaW4gdGhlIHBpcGVsaW5lLiBbKHNvdXJjZSldKGh0dHBzOi8vd3d3LnRpbWVzZnJlZXByZXNzLmNvbS9uZXdzLzIwMjIvanVsLzI4L2Jpa2UtY2hhdHRhbm9vZ2Jvb3N0cy1yaWRlcnNoaXAtb3Zlci0xMC15ZWFycy8pIAoKVXJiYW4gYmlrZXNoYXJlIHByb2dyYW1zIGhhdmUgZXZvbHZlZCBhcyBhIHJlc3BvbnNlIHRvIHRoZSBpbmNyZWFzaW5nIGRlbWFuZCBmb3Igc3VzdGFpbmFibGUgYW5kIGVmZmljaWVudCB1cmJhbiB0cmFuc3BvcnRhdGlvbi4gT3JpZ2luYXRpbmcgaW4gdGhlIG1pZC0yMHRoIGNlbnR1cnkgaW4gRXVyb3BlLCBlYXJseSBiaWtlc2hhcmUgc3lzdGVtcyBnYWluZWQgcG9wdWxhcml0eSBpbiB0aGUgMjFzdCBjZW50dXJ5IHdpdGggdGhlIGFkdmVudCBvZiB0ZWNobm9sb2d5IGFuZCBpbmNyZWFzZWQgZW52aXJvbm1lbnRhbCBjb25zY2lvdXNuZXNzLiBPbmUgb2YgdGhlIHBpb25lZXJpbmcgc3lzdGVtcyB3YXMgTHlvbidzIFbDqWxvJ3YgaW4gMjAwNSwgc2V0dGluZyB0aGUgc3RhZ2UgZm9yIGdsb2JhbCBhZG9wdGlvbi4gU3Vic2VxdWVudGx5LCBtYWpvciBjaXRpZXMgd29ybGR3aWRlIGludHJvZHVjZWQgdGhlaXIgb3duIGJpa2VzaGFyZSBwcm9ncmFtcy4gVGhlc2UgaW5pdGlhdGl2ZXMgdHlwaWNhbGx5IG9wZXJhdGUgdGhyb3VnaCBhIG5ldHdvcmsgb2Ygc3RyYXRlZ2ljYWxseSBwbGFjZWQgc3RhdGlvbnMgd2hlcmUgdXNlcnMgY2FuIHJlbnQgYmljeWNsZXMgZm9yIHNob3J0IHRyaXBzLiBVc2VycyBhY2Nlc3MgYmlrZXMgdGhyb3VnaCBtb2JpbGUgYXBwcyBvciBtZW1iZXJzaGlwIGNhcmRzLCB1bmxvY2tpbmcgdGhlbSBhdCBvbmUgc3RhdGlvbiBhbmQgcmV0dXJuaW5nIHRoZW0gYXQgYW5vdGhlci4gVGhlIHBheS1wZXItcmlkZSBvciBzdWJzY3JpcHRpb24tYmFzZWQgbW9kZWwgZW5jb3VyYWdlcyBzaG9ydC1kaXN0YW5jZSBjb21tdXRpbmcsIGNvbnRyaWJ1dGluZyB0byByZWR1Y2VkIHRyYWZmaWMgY29uZ2VzdGlvbiBhbmQgZW52aXJvbm1lbnRhbCBpbXBhY3Qgd2hpbGUgcHJvbW90aW5nIGEgaGVhbHRoaWVyIGxpZmVzdHlsZS4gCgpUaGUgc3VjY2VzcyBvZiB1cmJhbiBiaWtlc2hhcmUgcHJvZ3JhbXMgaGluZ2VzIG9uIHRoZWlyIGFjY2Vzc2liaWxpdHksIGVhc2Ugb2YgdXNlLCBhbmQgaW50ZWdyYXRpb24gd2l0aCBwdWJsaWMgdHJhbnNpdCBzeXN0ZW1zLiBSZWJhbGFuY2luZyBpcyBhIGNyaXRpY2FsIGxvZ2lzdGljcyBodXJkbGUgZm9yIGJpa2VzaGFyZSBwcm9ncmFtJ3MgcmVsaWFiaWxpdHkgYW5kIHN1Y2Nlc3MuIFNpbmNlIG1hbnkgY2l0aWVzIGV4cGVyaWVuY2UgYmlrZXNoYXJlICdydXNoIGhvdXJzJyBpdCBpcyBvZnRlbiB0aGUgY2FzZSB0aGF0IGJpa2VzIGFyZSBub3Qgd2hlcmUgcmlkZXJzIG5lZWQgdGhlbSB0byBiZS4gUmViYWxhbmNpbmcgaW52b2x2ZXMgdGhlIHJlZGlzdHJpYnV0aW9uIG9mIGJpY3ljbGVzIGFjcm9zcyBzdGF0aW9ucyB0byBhZGRyZXNzIHNwYXRpYWwgYW5kIHRlbXBvcmFsIHZhcmlhdGlvbnMgaW4gdXNlciBkZW1hbmQuIEN1cnJlbnRseSwgbWFueSBjaXRpZXMgZW1wbG95IGRlZGljYXRlZCB0ZWFtcyBvZiByZWJhbGFuY2luZyBzdGFmZiB3aG8gbWFudWFsbHkgdHJhbnNwb3J0IGJpa2VzIGJldHdlZW4gc3RhdGlvbnMgYmFzZWQgb24gcmVhbC10aW1lIHVzYWdlIHBhdHRlcm5zIGFuZCBwcmVkaWN0aXZlIG1vZGVscy4gSG93ZXZlciwgdGhlIG1hbnVhbCBuYXR1cmUgb2YgdGhpcyBwcm9jZXNzIHBvc2VzIGxvZ2lzdGljYWwgY2hhbGxlbmdlcywgaW5jbHVkaW5nIGhpZ2ggb3BlcmF0aW9uYWwgY29zdHMsIGluZWZmaWNpZW5jaWVzLCBhbmQgZGVsYXlzIGluIHJlc3BvbnNlIHRvIHN1ZGRlbiBkZW1hbmQgZmx1Y3R1YXRpb25zLiBUaGUgY2hhbGxlbmdlIGxpZXMgaW4gbWFpbnRhaW5pbmcgYSBkZWxpY2F0ZSBlcXVpbGlicml1bSBpbiBiaWtlIGRpc3RyaWJ1dGlvbiB0byBvcHRpbWl6ZSB1c2VyIGV4cGVyaWVuY2UsIG1ha2luZyBpdCBpbXBlcmF0aXZlIGZvciBiaWtlc2hhcmUgcHJvZ3JhbXMgdG8gZXhwbG9yZSBtb3JlIGVmZmljaWVudCBhbmQgZGF0YS1kcml2ZW4gcmViYWxhbmNpbmcgc3RyYXRlZ2llcy4KCiMgKipNZXRob2RzKioKClRoaXMgc3R1ZHkgZW1wbG95cyB0aHJlZSBwcmltYXJ5IGRhdGFzZXRzOiAKCjEpIEJpa2UgQ2hhdHRhbm9vZ2Egc3RhdGlvbiBhbmQgcmlkZXJzaGlwIGRhdGEgZnJvbSBTcHJpbmcgMjAyMgoyKSB3ZWF0aGVyIGRhdGEgY29sbGVjdGVkIGF0IENoYXR0YW5vb2dhIE1ldHJvcG9saXRhbiBBaXJwb3J0IGR1cmluZyB0aG9zZSBzYW1lIGRhdGVzIGFuZCBhY2Nlc3NlZCB2aWEgdGhlIHJpZW0gcGFja2FnZSBpbiBSCjMpIDIwMjEgNS1ZIEFtZXJpY2FuIENvbW11bml0eSBTdXJ2ZXkgZGF0YSBmb3IgSGFtaWx0b24gQ291bnR5LCBUTi4gCjQpIE9wZW4gU3RyZWV0cyBNYXBzIGRhdGEgYWJvdXQgaGlnaHdheSBhbmQgd2F0ZXJ3YXkgbG9jYXRpb25zLgoKIyMgRGF0YSBXcmFuZ2xpbmcKCldyYW5nbGUgdGhlIGRhdGEgdG8gYmUgb3BlcmFibGUgd2l0aCB0aGUgbHVicmlkYXRlIFIgcGFja2FnZS4KCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgcmVzdWx0cz0naGlkZSd9CmRhdCA8LSBzdF9yZWFkKCIvVXNlcnMvTGF1cmFGcmFuY2VzXzEvTGlicmFyeS9DbG91ZFN0b3JhZ2UvR29vZ2xlRHJpdmUtbGF1cmFtdWVsbGVyc29wcGFydEBnbWFpbC5jb20vTXkgRHJpdmUvUGVubi9Db3Vyc2VzL01VU0E1MDhfUFBBL1BQQV9Bc3NpZ25tZW50NS9CaWtlU2hhcmUvQmlrZV9DaGF0dGFub29nYV9UcmlwX0RhdGFfTWFyMjJfTWF5MjIuY3N2IikKCmRhdCRTdGFydC5UaW1lIDwtIGdzdWIoIi8iLCAiLSIsIGRhdCRTdGFydC5UaW1lKQpkYXQkU3RhcnQuVGltZSA8LSBnc3ViKCIgQU0kIiwgIiBBTSIsIGRhdCRTdGFydC5UaW1lKQpkYXQkU3RhcnQuVGltZSA8LSBnc3ViKCIgUE0kIiwgIiBQTSIsIGRhdCRTdGFydC5UaW1lKQpkYXQkRW5kLlRpbWUgPC0gZ3N1YigiLyIsICItIiwgZGF0JEVuZC5UaW1lKQpkYXQkRW5kLlRpbWUgPC0gZ3N1YigiIEFNJCIsICIgQU0iLCBkYXQkRW5kLlRpbWUpCmRhdCRFbmQuVGltZSA8LSBnc3ViKCIgUE0kIiwgIiBQTSIsIGRhdCRFbmQuVGltZSkKCiMgQ29udmVydCB0byAyNC1ob3VyIGNsb2NrCmRhdCRTdGFydC5UaW1lIDwtIG1keV9obXMoZGF0JFN0YXJ0LlRpbWUpCgojIEZvcm1hdCB0aGUgcmVzdWx0IGluIDI0LWhvdXIgY2xvY2sKZGF0JFN0YXJ0LlRpbWUgPC0gZm9ybWF0KGRhdCRTdGFydC5UaW1lLCAiJVktJW0tJWQgJUg6JU06JVMiKQoKI1BhcnNlIE91dCBmcm9tIExhdCBMb25nIApkYXQkZnJvbV9sb25naXR1ZGUgPC0gYXMubnVtZXJpYyhnc3ViKCJQT0lOVCBcXCgoLT9cXGQrXFwuXFxkKykgXFxkK1xcLlxcZCtcXCkiLCAiXFwxIiwgZGF0JFN0YXJ0LkxvY2F0aW9uKSkKZGF0JGZyb21fbGF0aXR1ZGUgPC0gYXMubnVtZXJpYyhnc3ViKCJQT0lOVCBcXCgtP1xcZCtcXC5cXGQrICgtP1xcZCtcXC5cXGQrKVxcKSIsICJcXDEiLCBkYXQkU3RhcnQuTG9jYXRpb24pKQpkYXQkdG9fbG9uZ2l0dWRlIDwtIGFzLm51bWVyaWMoZ3N1YigiUE9JTlQgXFwoKC0/XFxkK1xcLlxcZCspIFxcZCtcXC5cXGQrXFwpIiwgIlxcMSIsIGRhdCRFbmQuTG9jYXRpb24pKQpkYXQkdG9fbGF0aXR1ZGUgPC0gYXMubnVtZXJpYyhnc3ViKCJQT0lOVCBcXCgtP1xcZCtcXC5cXGQrICgtP1xcZCtcXC5cXGQrKVxcKSIsICJcXDEiLCBkYXQkRW5kLkxvY2F0aW9uKSkKCiNDcmVhdGUgdGltZSBiaW5zIApkYXQyIDwtIGRhdCAlPiUKICBtdXRhdGUoaW50ZXJ2YWw2MCA9IGZsb29yX2RhdGUoeW1kX2htcyhTdGFydC5UaW1lKSwgdW5pdCA9ICJob3VyIiksICNkb3duc2lkZSBpcyB0aGF0IGlmIGl0J3MgNzo1OSwgaXRsbCBiZSA3OjAwIAogICAgICAgICBpbnRlcnZhbDE1ID0gZmxvb3JfZGF0ZSh5bWRfaG1zKFN0YXJ0LlRpbWUpLCB1bml0ID0gIjE1IG1pbnMiKSwKICAgICAgICAgd2VlayA9IHdlZWsoaW50ZXJ2YWw2MCksCiAgICAgICAgIGRvdHcgPSB3ZGF5KGludGVydmFsNjAsIGxhYmVsPVRSVUUpKQpgYGAKCkxvYWQgaW4gMjAyMSA1WSBBbWVyaWNhbiBDb21tdW5pdHkgU3VydmV5IENlbnN1cyBEYXRhIGZvciBIYW1pbHRvbiBDb3VudHkuIFdlIGV2YWx1YXRlIHRoZSBmb2xsb3dpbmcgdmFyaWFibGVzOiBUb3RhbCBwb3B1bGF0aW9uLCBtZWRpYW4gaG91c2Vob2xkIGluY29tZSwgbWVkaWFuIGFnZSwgd2hpdGUgdnMgbm9uLXdoaXRlIHBvcHVsYXRpb25zLCBwZXJjZW50IHB1YmxpYyB0cmFuc2l0IGNvbW11dGVycywgYW5kIG1lZGlhbiBjb21tdXRlIHRpbWUuIAoKYGBge3IgZ2V0X2NlbnN1cywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY2FjaGU9VFJVRSwgcmVzdWx0cyA9ICdoaWRlJ30KY2hhdENlbnN1cyA8LSAKICBnZXRfYWNzKGdlb2dyYXBoeSA9ICJ0cmFjdCIsIAogICAgICAgICAgdmFyaWFibGVzID0gYygiQjAxMDAzXzAwMSIsICJCMTkwMTNfMDAxIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICJCMDIwMDFfMDAyIiwgIkIwODAxM18wMDEiLAogICAgICAgICAgICAgICAgICAgICAgICAiQjA4MDEyXzAwMSIsICJCMDgzMDFfMDAxIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICJCMDgzMDFfMDEwIiwgIkIwMTAwMl8wMDEiKSwgCiAgICAgICAgICB5ZWFyID0gMjAyMSwgCiAgICAgICAgICBzdGF0ZSA9IDQ3LCAKICAgICAgICAgIGNvdW50eT0gMDY1LAogICAgICAgICAgZ2VvbWV0cnkgPSBUUlVFLCAKICAgICAgICAgIG91dHB1dCA9ICJ3aWRlIikgJT4lCiAgcmVuYW1lKFRvdGFsX1BvcCA9ICBCMDEwMDNfMDAxRSwKICAgICAgICAgTWVkX0luYyA9IEIxOTAxM18wMDFFLAogICAgICAgICBNZWRfQWdlID0gQjAxMDAyXzAwMUUsCiAgICAgICAgIFdoaXRlX1BvcCA9IEIwMjAwMV8wMDJFLAogICAgICAgICBUcmF2ZWxfVGltZSA9IEIwODAxM18wMDFFLAogICAgICAgICBOdW1fQ29tbXV0ZXJzID0gQjA4MDEyXzAwMUUsCiAgICAgICAgIE1lYW5zX29mX1RyYW5zcG9ydCA9IEIwODMwMV8wMDFFLAogICAgICAgICBUb3RhbF9QdWJsaWNfVHJhbnMgPSBCMDgzMDFfMDEwRSkgJT4lCiAgc2VsZWN0KFRvdGFsX1BvcCwgTWVkX0luYywgV2hpdGVfUG9wLCBUcmF2ZWxfVGltZSwKICAgICAgICAgTWVhbnNfb2ZfVHJhbnNwb3J0LCBUb3RhbF9QdWJsaWNfVHJhbnMsCiAgICAgICAgIE1lZF9BZ2UsCiAgICAgICAgIEdFT0lELCBnZW9tZXRyeSkgJT4lCiAgbXV0YXRlKFBlcmNlbnRfV2hpdGUgPSBXaGl0ZV9Qb3AgLyBUb3RhbF9Qb3AsCiAgICAgICAgIE1lYW5fQ29tbXV0ZV9UaW1lID0gVHJhdmVsX1RpbWUgLyBUb3RhbF9QdWJsaWNfVHJhbnMsCiAgICAgICAgIFBlcmNlbnRfVGFraW5nX1B1YmxpY19UcmFucyA9IFRvdGFsX1B1YmxpY19UcmFucyAvIE1lYW5zX29mX1RyYW5zcG9ydCkKCiNleHRyYWN0IGdlb21ldHJpZXMgCgpjaXR5X2xpbWl0cyA8LSBzdF9yZWFkKCIvVXNlcnMvTGF1cmFGcmFuY2VzXzEvTGlicmFyeS9DbG91ZFN0b3JhZ2UvR29vZ2xlRHJpdmUtbGF1cmFtdWVsbGVyc29wcGFydEBnbWFpbC5jb20vTXkgRHJpdmUvUGVubi9Db3Vyc2VzL01VU0E1MDhfUFBBL1BQQV9Bc3NpZ25tZW50NS9CaWtlU2hhcmUvQ2hhdHRhbm9vZ2EgQ2l0eSBMaW1pdHMvZ2VvX2V4cG9ydF80OGY0NDllNi1mMDMzLTRmYzctYWZhMy1iZTczYmNlNzRjMWQuc2hwIikgJT4lIAogIHN0X3RyYW5zZm9ybShjcnM9c3RfY3JzKGNoYXRDZW5zdXMpKSAKCmNpdHlfbGltaXRzIDwtIHN0X21ha2VfdmFsaWQoY2l0eV9saW1pdHMpCmNoYXRDZW5zdXMgPC0gc3RfbWFrZV92YWxpZChjaGF0Q2Vuc3VzKQoKIyBQZXJmb3JtIHRoZSBpbnRlcnNlY3Rpb24KaW50ZXJzZWN0ZWRfY2hhdFRyYWN0cyA8LSBzdF9pbnRlcnNlY3Rpb24oY2hhdENlbnN1cywgY2l0eV9saW1pdHMpCgojIEV4dHJhY3QgZ2VvbWV0cmllcyBmb3IgdGhlIGludGVyc2VjdGVkIHRyYWN0cwpjaGF0VHJhY3RzIDwtIAogIGludGVyc2VjdGVkX2NoYXRUcmFjdHMgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIGRpc3RpbmN0KEdFT0lELCAua2VlcF9hbGwgPSBUUlVFKSAlPiUKICBzZWxlY3QoR0VPSUQsIGdlb21ldHJ5KSAlPiUgCiAgc3Rfc2YKCiNhZGQgY2Vuc3VzIHRyYWN0cyAKZGF0X2NlbnN1cyA8LSBzdF9qb2luKGRhdDIgJT4lIAogICAgICAgICAgZmlsdGVyKGlzLm5hKGZyb21fbG9uZ2l0dWRlKSA9PSBGQUxTRSAmCiAgICAgICAgICAgICAgICAgICBpcy5uYShmcm9tX2xhdGl0dWRlKSA9PSBGQUxTRSAmCiAgICAgICAgICAgICAgICAgICBpcy5uYSh0b19sYXRpdHVkZSkgPT0gRkFMU0UgJgogICAgICAgICAgICAgICAgICAgaXMubmEodG9fbG9uZ2l0dWRlKSA9PSBGQUxTRSkgJT4lCiAgICAgICAgICBzdF9hc19zZiguLCBjb29yZHMgPSBjKCJmcm9tX2xvbmdpdHVkZSIsICJmcm9tX2xhdGl0dWRlIiksIGNycyA9IDQzMjYpLAogICAgICAgIGNoYXRUcmFjdHMgJT4lCiAgICAgICAgICBzdF90cmFuc2Zvcm0oY3JzPTQzMjYpLAogICAgICAgIGpvaW49c3RfaW50ZXJzZWN0cywKICAgICAgICAgICAgICBsZWZ0ID0gVFJVRSkgJT4lCiAgcmVuYW1lKE9yaWdpbi5UcmFjdCA9IEdFT0lEKSAlPiUKICBtdXRhdGUoZnJvbV9sb25naXR1ZGUgPSB1bmxpc3QobWFwKGdlb21ldHJ5LCAxKSksCiAgICAgICAgIGZyb21fbGF0aXR1ZGUgPSB1bmxpc3QobWFwKGdlb21ldHJ5LCAyKSkpJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIHNlbGVjdCgtZ2VvbWV0cnkpJT4lCiAgc3RfYXNfc2YoLiwgY29vcmRzID0gYygidG9fbG9uZ2l0dWRlIiwgInRvX2xhdGl0dWRlIiksIGNycyA9IDQzMjYpICU+JQogIHN0X2pvaW4oLiwgY2hhdFRyYWN0cyAlPiUKICAgICAgICAgICAgc3RfdHJhbnNmb3JtKGNycz00MzI2KSwKICAgICAgICAgIGpvaW49c3RfaW50ZXJzZWN0cywKICAgICAgICAgIGxlZnQgPSBUUlVFKSAlPiUKICByZW5hbWUoRGVzdGluYXRpb24uVHJhY3QgPSBHRU9JRCkgICU+JQogIG11dGF0ZSh0b19sb25naXR1ZGUgPSB1bmxpc3QobWFwKGdlb21ldHJ5LCAxKSksCiAgICAgICAgIHRvX2xhdGl0dWRlID0gdW5saXN0KG1hcChnZW9tZXRyeSwgMikpKSU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBzZWxlY3QoLWdlb21ldHJ5KQpgYGAKCkxvYWQgaW4gd2VhdGhlciBkYXRhIGZyb20gQ2hhdHRhbm9vZ2EgTWV0cm9wb2xpdGFuIEFpcnBvcnQncyB3ZWF0aGVyIHN0YXRpb24gZm9yIE1hcmNoIDAxIHRvIE1heSAzMSwgMjAyMi4gQ2hhdHRhbm9vZ2EgaGFzIGEgZGlzdGluY3Qgc3ByaW5nIHNlYXNvbiB3aGVyZSBieSBtaWQtQXByaWwgcHJlY2lwaXRhdGlvbiBmYWxscyBhbmQgdGVtcGVyYXR1cmVzIHN0ZWFkaWx5IHJpc2UuIAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY2FjaGU9VFJVRX0Kd2VhdGhlci5QYW5lbCA8LSAKICByaWVtX21lYXN1cmVzKHN0YXRpb24gPSAiS0NIQSIsIGRhdGVfc3RhcnQgPSAiMjAyMi0wMy0wMSIsIGRhdGVfZW5kID0gIjIwMjItMDUtMzEiKSAlPiUKICBkcGx5cjo6c2VsZWN0KHZhbGlkLCB0bXBmLCBwMDFpLCBza250KSU+JQogIHJlcGxhY2UoaXMubmEoLiksIDApICU+JQogICAgbXV0YXRlKGludGVydmFsNjAgPSB5bWRfaChzdWJzdHIodmFsaWQsMSwxMykpKSAlPiUKICAgIG11dGF0ZSh3ZWVrID0gd2VlayhpbnRlcnZhbDYwKSwKICAgICAgICAgICBkb3R3ID0gd2RheShpbnRlcnZhbDYwLCBsYWJlbD1UUlVFKSkgJT4lCiAgICBncm91cF9ieShpbnRlcnZhbDYwKSAlPiUKICAgIHN1bW1hcml6ZShUZW1wZXJhdHVyZSA9IG1heCh0bXBmKSwKICAgICAgICAgICAgICBQcmVjaXBpdGF0aW9uID0gc3VtKHAwMWkpLAogICAgICAgICAgICAgIFdpbmRfU3BlZWQgPSBtYXgoc2tudCkpICU+JQogICAgbXV0YXRlKFRlbXBlcmF0dXJlID0gaWZlbHNlKFRlbXBlcmF0dXJlID09IDAsIDQyLCBUZW1wZXJhdHVyZSkpCgpncmlkLmFycmFuZ2UoCiAgZ2dwbG90KHdlYXRoZXIuUGFuZWwsIGFlcyhpbnRlcnZhbDYwLFByZWNpcGl0YXRpb24pKSArIGdlb21fbGluZSgpICsgCiAgbGFicyh0aXRsZT0iUGVyY2lwaXRhdGlvbiIsIHg9IkhvdXIiLCB5PSJQZXJlY2lwaXRhdGlvbiIpICsgcGxvdFRoZW1lLAogIGdncGxvdCh3ZWF0aGVyLlBhbmVsLCBhZXMoaW50ZXJ2YWw2MCxXaW5kX1NwZWVkKSkgKyBnZW9tX2xpbmUoKSArIAogICAgbGFicyh0aXRsZT0iV2luZCBTcGVlZCIsIHg9IkhvdXIiLCB5PSJXaW5kIFNwZWVkIikgKyBwbG90VGhlbWUsCiAgZ2dwbG90KHdlYXRoZXIuUGFuZWwsIGFlcyhpbnRlcnZhbDYwLFRlbXBlcmF0dXJlKSkgKyBnZW9tX2xpbmUoKSArIAogICAgbGFicyh0aXRsZT0iVGVtcGVyYXR1cmUiLCB4PSJIb3VyIiwgeT0iVGVtcGVyYXR1cmUiKSArIHBsb3RUaGVtZSwKICB0b3A9IldlYXRoZXIgRGF0YSAtIENoYXR0YW5vb2dhIEtDSEEgLSBTcHJpbmcgMjAyMiIpCgoKIyAjIENyZWF0ZSBhIHRyZW5kIGxpbmUgZm9yIHdlZWtseSByaWRlcyB0byBhZGQgb24gdG9wIG9mIHdlYXRoZXIgCiMgdHJlbmRfbGluZSA8LSBnZ3Bsb3QoZGF0X2NlbnN1cyAlPiUKIyAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwX2J5KHdlZWspICU+JQojICAgICAgICAgICAgICAgICAgICAgICAgdGFsbHkoKSwKIyAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IHdlZWssIHkgPSBuKSkgKwojICAgZ2VvbV9saW5lKHNpemUgPSAwLjIpCgpgYGAKCiMjIEV4cGxvcmF0b3J5IEFuYWx5c2lzCgoKQmlrZSBDaGF0dGFub29nYSBoYXMgNDIgc3RhdGlvbnMgdGhhdCBhcmUgY29uY2VudHJhdGVkIGluIHRoZSBEb3dudG93biBhbmQgTm9ydGggQ2hhdHRhbm9vZ2EgbmVpZ2hib3Job29kcy4gCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjYWNoZT1UUlVFLCBmaWcud2lkdGg9MTB9CgpoaWdod2F5cyA8LSBvcHEoIkNoYXR0YW5vb2dhLCBUTiIpICU+JSAKICBhZGRfb3NtX2ZlYXR1cmUoa2V5ID0gImhpZ2h3YXkiLCB2YWx1ZSA9IGMoIm1vdG9yd2F5IiwgIm1vdG9yd2F5X2xpbmsiLCAidHJ1bmsiLCAicHJpbWFyeSIpKSAlPiUgCiAgb3NtZGF0YV9zZigpCgpyaXZlcnMgPC0gb3BxKCJDaGF0dGFub29nYSwgVE4iKSAlPiUgCiAgYWRkX29zbV9mZWF0dXJlKGtleSA9ICd3YXRlcndheScsIHZhbHVlID0gInJpdmVyIikgJT4lCiAgb3NtZGF0YV9zZigpCgpnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhID0gY2hhdFRyYWN0cyAlPiUgc3RfdHJhbnNmb3JtKGNycyA9IDQzMjYpLCBmaWxsID0gImRhcmtncmF5IiwgY29sb3IgPSAiZ3JheTkwIikgKwogIGdlb21fc2YoZGF0YSA9IGhpZ2h3YXlzJG9zbV9saW5lcywgY29sb3IgPSAiZ3JheTMwIiwgc2l6ZSA9IDAuMywgYWxwaGEgPSAwLjUpICsKICBnZW9tX3NmKGRhdGEgPSByaXZlcnMkb3NtX3BvbHlnb25zLCBjb2xvciA9ICJsaWdodGJsdWUiLCBhbHBoYSA9IDAuNSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IGRhdDIsIGFlcyh4ID0gZnJvbV9sb25naXR1ZGUsIHkgPSBmcm9tX2xhdGl0dWRlKSwgCiAgICAgICAgICAgICBjb2xvciA9ICJibHVlIiwgYWxwaGEgPSAwLjcsIHNpemUgPSAwLjMpICsKICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBCaWtlc2hhcmUgU3RhdGlvbnMgaW4gQ2hhdHRhbm9vZ2EiLAogICAgICAgc3VidGl0bGUgPSAiTWFyY2ggLSBNYXkgMjAyMiB8IERhcmsgZ3JheSBsaW5lcyBhcmUgbWFqb3IgbW90b3J3YXlzIHwgTGlnaHQgZ3JheSBsaW5lcyBhcmUgY2Vuc3VzIHRyYWN0cyIpKyAKICBtYXBUaGVtZQoKIyAjc3RhdGlvbiBieSByaWRlcyAtIHRyaWVkIHRvIHNob3cgc3RhdGlvbiBwb3B1bGFyaXR5CiMgZ2dwbG90KCkrCiMgICBnZW9tX3NmKGRhdGEgPSBjaGF0VHJhY3RzICU+JSBzdF90cmFuc2Zvcm0oY3JzID0gNDMyNiksIGZpbGwgPSAiZGFya2dyYXkiLCBjb2xvciA9ICJncmF5OTAiKSArCiMgICBnZW9tX3BvaW50KGRhdGEgPSBkYXRfY2Vuc3VzICU+JQojICAgICAgICAgICAgICAgZ3JvdXBfYnkoU3RhcnQuU3RhdGlvbi5JRCkgJT4lCiMgICAgICAgICAgICAgICBzdW1tYXJpemUoYXZnX3dlZWtseV9yaWRlcyA9IG1lYW4obikpLAojICAgICAgICAgICAgIGFlcyh4ID0gZnJvbV9sb25naXR1ZGUsIHkgPSBmcm9tX2xhdGl0dWRlLCBjb2xvciA9IGF2Z193ZWVrbHlfcmlkZXMsIHNpemUgPSBhdmdfd2Vla2x5X3JpZGVzKSwKIyAgICAgICAgICAgICBhbHBoYSA9IDEpICsKIyAgICMgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAiI0QyRkJENCIsIGhpZ2ggPSAiIzUyN0Q4MiIsIG5hLnZhbHVlID0gInRyYW5zcGFyZW50IikgKwojICAgc2NhbGVfY29sb3VyX3ZpcmlkaXMoZGlyZWN0aW9uID0gLTEsIGRpc2NyZXRlID0gRkFMU0UsIG9wdGlvbiA9ICJHIikrCiMgICB5bGltKG1pbihkYXRfY2Vuc3VzJGZyb21fbGF0aXR1ZGUpLCBtYXgoZGF0X2NlbnN1cyRmcm9tX2xhdGl0dWRlKSkrCiMgICB4bGltKG1pbihkYXRfY2Vuc3VzJGZyb21fbG9uZ2l0dWRlKSwgbWF4KGRhdF9jZW5zdXMkZnJvbV9sb25naXR1ZGUpKSsKIyAgIGxhYnModGl0bGU9IkF2ZyBXZWVrbHkgQmlrZSBzaGFyZSB0cmlwcyBieSBzdGF0aW9uIiwKIyAgICAgICAgICAgc3VidGl0bGUgPSAiTWFyY2ggLSBNYXkgMjAyMiB8IENoYXR0YW5vb2dhLCBUTiIpKwojICAgbWFwVGhlbWUKYGBgCgojIyMgVGVtcG9yYWwgQW5hbHlzaXMgCgpFeGFtaW5pbmcgb3VyIGJpa2Ugc2hhcmUgZGF0YSwgd2Ugbm90aWNlIGNsZWFyIHRlbXBvcmFsIHRyZW5kczogcGVha3MgZHVyaW5nIGNlcnRhaW4gaG91cnMgb2YgdGhlIGRheSBhbmQgYSBsb25nZXItdGltZSBkZWNsaW5lIGluIHRyaXBzIGFjcm9zcyB0aGUgc2Vhc29uLiBUaGVyZSBpcyBhIHNwaWtlIG9mIGFjdGl2aXR5IGluIGVhcmx5IE1hcmNoIGR1cmluZyB0aGUgRXJsYW5nZXIgQ2hhdHRhbm9vZ2EgTWFyYXRob24gd2Vla2VuZC4gR2VuZXJhbGx5LCB3ZWVrZW5kIGRheXMgYXJlIG1vcmUgcG9wdWxhciBmb3IgcmlkaW5nIEJpa2UgQ2hhdHRhbm9vZ2EgdGhhbiB3ZWVrZGF5IGRheXMuIAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZ2dwbG90KGRhdF9jZW5zdXMgJT4lCiAgICAgICAgIGdyb3VwX2J5KGludGVydmFsNjApICU+JQogICAgICAgICB0YWxseSgpKSsKICBnZW9tX2xpbmUoYWVzKHggPSBpbnRlcnZhbDYwLCB5ID0gbiksIHNpemU9MC4yKSsKICBsYWJzKHRpdGxlPSJCaWtlIHNoYXJlIHRyaXBzIHBlciBob3VyIiwKICAgICAgIHN1YnRpdGxlID0gIk1hcmNoIC0gTWF5IDIwMjIgfCBDaGF0dGFub29nYSwgVE4iLAogICAgICAgeD0iRGF0ZSIsIAogICAgICAgeT0iTnVtYmVyIG9mIHRyaXBzIikrCiAgcGxvdFRoZW1lCgpgYGAKCkJyZWFraW5nIHRoZXNlIHRyZW5kcyBkb3duIHRvIHRoZSwgd2Ugbm90aWNlIHBlYWtzIGF0IGNlcnRhaW4gdGltZXMgb2YgZGF5IGFuZCBjZXJ0YWluIGRheXMgb2YgdGhlIHdlZWsuIE92ZXJhbGwgcmlkZXJzaGlwIGlzIGhpZ2hlciBvbiB0aGUgd2Vla2VuZCwgYW5kIHBlYWtzIGEgZmV3IGhvdXJzIGVhcmxpZXIgY29tcGFyZWQgdG8gdGhlIHdlZWtkYXkncyBhZnRlciB3b3JrIHJ1c2guIEdlbmVyYWxseSwgYWZ0ZXJub29uIHJ1c2ggaG91cnMgYXJlIGFzc29jaWF0ZWQgd2l0aCB0aGUgaGlnaGVzdCByaWRlcnNoaXAgdm9sdW1lcyBkdXJpbmcgdGhlIHdlZWsuCgpgYGB7ciB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSB9CmdncGxvdChkYXRfY2Vuc3VzICU+JSBtdXRhdGUoaG91ciA9IGhvdXIoU3RhcnQuVGltZSkpKSsKICAgICBnZW9tX2ZyZXFwb2x5KGFlcyhob3VyLCBjb2xvciA9IGRvdHcpLCBiaW53aWR0aCA9IDEpKwogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIiNENzRFMDkiLCAiI2JkZDdlNyIsIiM2YmFlZDYiLCIjMzE4MmJkIiwiIzA4NTE5YyIsICJibHVlIiwgIiM2RTBFMEEiKSwgbmFtZSA9ICJEYXkiKSArIAogIGxhYnModGl0bGU9IkJpa2Ugc2hhcmUgdHJpcHMsIGJ5IGRheSBvZiB0aGUgd2VlayIsCiAgICAgIHN1YnRpdGxlID0gIk1hcmNoIC0gTWF5IDIwMjIgfCBDaGF0dGFub29nYSwgVE4iLAogICAgICAgeD0iSG91ciIsIAogICAgICAgeT0iVHJpcCBDb3VudHMiKSsKICAgICBwbG90VGhlbWUKCgpnZ3Bsb3QoZGF0X2NlbnN1cyAlPiUgCiAgICAgICAgIG11dGF0ZShob3VyID0gaG91cihTdGFydC5UaW1lKSwKICAgICAgICAgICAgICAgIHdlZWtlbmQgPSBpZmVsc2UoZG90dyAlaW4lIGMoIlN1biIsICJTYXQiKSwgIldlZWtlbmQiLCAiV2Vla2RheSIpKSkrCiAgICAgZ2VvbV9mcmVxcG9seShhZXMoaG91ciwgY29sb3IgPSB3ZWVrZW5kKSwgYmlud2lkdGggPSAxKSsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiIzMxODJiZCIsICIjRDc0RTA5IikpICsKICBsYWJzKHRpdGxlPSJCaWtlIHNoYXJlIHRyaXBzIC0gd2Vla2VuZCB2cyB3ZWVrZGF5IiwKICAgICAgICBzdWJ0aXRsZSA9ICJNYXJjaCAtIE1heSAyMDIyIHwgQ2hhdHRhbm9vZ2EsIFROIiwKICAgICAgIHg9IkhvdXIiLCAKICAgICAgIHk9IlRyaXAgQ291bnRzIikrCiAgICAgcGxvdFRoZW1lCmBgYAoKYGBge3Igd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UgfQpkYXRfY2Vuc3VzICU+JQogICAgICAgIG11dGF0ZSh0aW1lX29mX2RheSA9IGNhc2Vfd2hlbihob3VyKGludGVydmFsNjApIDwgNyB8IGhvdXIoaW50ZXJ2YWw2MCkgPiAxOCB+ICJPdmVybmlnaHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBob3VyKGludGVydmFsNjApID49IDcgJiBob3VyKGludGVydmFsNjApIDwgMTAgfiAiQU0gUnVzaCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvdXIoaW50ZXJ2YWw2MCkgPj0gMTAgJiBob3VyKGludGVydmFsNjApIDwgMTUgfiAiTWlkLURheSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvdXIoaW50ZXJ2YWw2MCkgPj0gMTUgJiBob3VyKGludGVydmFsNjApIDw9IDE4IH4gIlBNIFJ1c2giKSklPiUKICAgICAgICAgZ3JvdXBfYnkoaW50ZXJ2YWw2MCwgU3RhcnQuU3RhdGlvbi5OYW1lLCB0aW1lX29mX2RheSkgJT4lCiAgICAgICAgIHRhbGx5KCklPiUKICBncm91cF9ieShTdGFydC5TdGF0aW9uLk5hbWUsIHRpbWVfb2ZfZGF5KSU+JQogIHN1bW1hcml6ZShtZWFuX3RyaXBzID0gbWVhbihuKSklPiUKICBnZ3Bsb3QoKSsKICBnZW9tX2hpc3RvZ3JhbShhZXMobWVhbl90cmlwcywgZmlsbD10aW1lX29mX2RheSksIGJpbndpZHRoID0gMSkrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsIG5hbWU9IlRpbWUgb2YgRGF5IikgKyAKICBsYWJzKHRpdGxlPSJNZWFuIE51bWJlciBvZiBIb3VybHkgVHJpcHMgUGVyIFN0YXRpb24iLAogICAgICAgc3VidGl0bGUgPSAiTWFyY2ggLSBNYXkgMjAyMiB8IENoYXR0YW5vb2dhLCBUTiIsCiAgICAgICB4PSJOdW1iZXIgb2Ygc3RhdGlvbnMiLCAKICAgICAgIHk9IlRyaXBzIEZyZXF1ZW5jeSIpKwogIGZhY2V0X3dyYXAofnRpbWVfb2ZfZGF5KSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBwbG90VGhlbWUKYGBgCgpgYGB7ciBpbmNsdWRlID0gRkFMU0UgfQpnZ3Bsb3QoZGF0X2NlbnN1cyAlPiUKICAgICAgICAgZ3JvdXBfYnkoaW50ZXJ2YWw2MCwgU3RhcnQuU3RhdGlvbi5OYW1lKSAlPiUKICAgICAgICAgdGFsbHkoKSkrCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKG4pLCBiaW53aWR0aCA9IDUsIGZpbGw9IiMzMTgyYmQiKSsKICBsYWJzKHRpdGxlPSJCaWtlIHNoYXJlIHRyaXBzIHBlciBociBieSBzdGF0aW9uIiwKICAgICAgICBzdWJ0aXRsZSA9ICJNYXJjaCAtIE1heSAyMDIyIHwgQ2hhdHRhbm9vZ2EsIFROIiwKICAgICAgIHg9Ik51bWJlciBvZiBTdGF0aW9ucyIsIAogICAgICAgeT0iVHJpcCBDb3VudHMiKSsKICBwbG90VGhlbWUKYGBgCgojIyMgU3BhdGlhbCBBbmFseXNpcwoKU3BhdGlhbGx5LCB3ZSBvYnNlcnZlIHRoYXQgdGhlIGhpZ2hlc3QgcmlkZXJzaGlwIHBlciBzdGF0aW9uIGNsdXN0ZXJzIGluIHRoZSBEb3dudG93biBhbmQgTm9ydGggQ2hhdHRhbm9vZ2EgYXJlYXMsIHBhcnRpY3VsYXJseSBpbiB0aGUgbWlkLWRheSBhbmQgYWZ0ZXJub29uLiAKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UgfQpnZ3Bsb3QoKSsKICBnZW9tX3NmKGRhdGEgPSBjaGF0VHJhY3RzICU+JSBzdF90cmFuc2Zvcm0oY3JzID0gNDMyNiksIGZpbGwgPSAiZGFya2dyYXkiLCBjb2xvciA9ICJncmF5OTAiKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZGF0X2NlbnN1cyAlPiUgCiAgICAgICAgICAgIG11dGF0ZShob3VyID0gaG91cihTdGFydC5UaW1lKSwKICAgICAgICAgICAgICAgIHdlZWtlbmQgPSBpZmVsc2UoZG90dyAlaW4lIGMoIlN1biIsICJTYXQiKSwgIldlZWtlbmQiLCAiV2Vla2RheSIpLAogICAgICAgICAgICAgICAgdGltZV9vZl9kYXkgPSBjYXNlX3doZW4oaG91cihpbnRlcnZhbDYwKSA8IDcgfCBob3VyKGludGVydmFsNjApID4gMTggfiAiT3Zlcm5pZ2h0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG91cihpbnRlcnZhbDYwKSA+PSA3ICYgaG91cihpbnRlcnZhbDYwKSA8IDEwIH4gIkFNIFJ1c2giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBob3VyKGludGVydmFsNjApID49IDEwICYgaG91cihpbnRlcnZhbDYwKSA8IDE1IH4gIk1pZC1EYXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBob3VyKGludGVydmFsNjApID49IDE1ICYgaG91cihpbnRlcnZhbDYwKSA8PSAxOCB+ICJQTSBSdXNoIikpJT4lCiAgICAgICAgICAgICAgZ3JvdXBfYnkoU3RhcnQuU3RhdGlvbi5JRCwgZnJvbV9sYXRpdHVkZSwgZnJvbV9sb25naXR1ZGUsIHdlZWtlbmQsIHRpbWVfb2ZfZGF5KSAlPiUKICAgICAgICAgICAgICB0YWxseSgpLAogICAgICAgICAgICBhZXMoeD1mcm9tX2xvbmdpdHVkZSwgeSA9IGZyb21fbGF0aXR1ZGUsIGNvbG9yID0gbiksIAogICAgICAgICAgICBmaWxsID0gInRyYW5zcGFyZW50IiwgYWxwaGEgPSAxLCBzaXplID0gMC4zKSsKICAjIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIiNEMkZCRDQiLCBoaWdoID0gIiM1MjdEODIiLCBuYS52YWx1ZSA9ICJ0cmFuc3BhcmVudCIpICsKICBzY2FsZV9jb2xvdXJfdmlyaWRpcyhkaXJlY3Rpb24gPSAtMSwgZGlzY3JldGUgPSBGQUxTRSwgb3B0aW9uID0gIkciKSsKICB5bGltKG1pbihkYXRfY2Vuc3VzJGZyb21fbGF0aXR1ZGUpLCBtYXgoZGF0X2NlbnN1cyRmcm9tX2xhdGl0dWRlKSkrCiAgeGxpbShtaW4oZGF0X2NlbnN1cyRmcm9tX2xvbmdpdHVkZSksIG1heChkYXRfY2Vuc3VzJGZyb21fbG9uZ2l0dWRlKSkrCiAgZmFjZXRfZ3JpZCh3ZWVrZW5kIH4gdGltZV9vZl9kYXkpKwogIGxhYnModGl0bGU9IkJpa2Ugc2hhcmUgdHJpcHMgcGVyIGhyIGJ5IHN0YXRpb24iLAogICAgICAgICAgc3VidGl0bGUgPSAiTWFyY2ggLSBNYXkgMjAyMiB8IENoYXR0YW5vb2dhLCBUTiIpKwogIG1hcFRoZW1lCmBgYAoKCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKI21hcHBpbmcgZGVtb2dyYXBoaWNzIGluIGNoYXR0YW5vb2dhIHRvIGJldHRlciB1bmRlcnN0YW5kIHRoZSBjaXR5J3MgZHluYW1pY3MgCgojY2hhdHRhbm9vZ2EgdHJhY3RzCmludGVyc2VjdGVkX2NoYXRDZW5zdXMgPC0gc3RfaW50ZXJzZWN0aW9uKGNoYXRDZW5zdXMsIGNoYXRUcmFjdHMpCgpwbG90MSA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhID0gY2hhdFRyYWN0cyAlPiUgc3RfdHJhbnNmb3JtKGNycyA9IDQzMjYpLCBmaWxsID0gImRhcmtncmF5IiwgY29sb3IgPSAiZ3JheTkwIikgKwogIGdlb21fc2YoZGF0YSA9IGludGVyc2VjdGVkX2NoYXRDZW5zdXMsIGFlcyhmaWxsID0gVG90YWxfUG9wKSwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMC4wNSkgKwogIGdlb21fc2YoZGF0YSA9IGhpZ2h3YXlzJG9zbV9saW5lcywgY29sb3IgPSAiZ3JheTMwIiwgc2l6ZSA9IDAuMywgYWxwaGEgPSAwLjUpICsKICBnZW9tX3NmKGRhdGEgPSByaXZlcnMkb3NtX3BvbHlnb25zLCBjb2xvciA9ICJsaWdodGJsdWUiLCBhbHBoYSA9IDAuNSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IGRhdDIsIGFlcyh4ID0gZnJvbV9sb25naXR1ZGUsIHkgPSBmcm9tX2xhdGl0dWRlKSwgCiAgICAgICAgICAgICBjb2xvciA9ICJibHVlIiwgYWxwaGEgPSAwLjcsIHNpemUgPSAwLjMpICsKICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICIjRDJGQkQ0IiwgaGlnaCA9ICIjNTI3RDgyIiwgbmEudmFsdWUgPSAidHJhbnNwYXJlbnQiKSArCiAgbGFicyh0aXRsZSA9ICJUcmFjdHMgYnkgVG90YWwgUG9wdWxhdGlvbiIsIHN1YnRpdGxlID0gIjIwMjIgfCBDaGF0dGFub29nYSwgVE4iKSArCiAgeWxpbShtaW4oZGF0X2NlbnN1cyRmcm9tX2xhdGl0dWRlKSwgbWF4KGRhdF9jZW5zdXMkZnJvbV9sYXRpdHVkZSkpKwogIHhsaW0obWluKGRhdF9jZW5zdXMkZnJvbV9sb25naXR1ZGUpLCBtYXgoZGF0X2NlbnN1cyRmcm9tX2xvbmdpdHVkZSkpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgbWFwVGhlbWUKCnBsb3QyIDwtIGdncGxvdCgpICsKICBnZW9tX3NmKGRhdGEgPSBjaGF0VHJhY3RzICU+JSBzdF90cmFuc2Zvcm0oY3JzID0gNDMyNiksIGZpbGwgPSAiZGFya2dyYXkiLCBjb2xvciA9ICJncmF5OTAiKSArCiAgZ2VvbV9zZihkYXRhID0gaW50ZXJzZWN0ZWRfY2hhdENlbnN1cywgYWVzKGZpbGwgPSBNZWRfSW5jKSwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMC4wNSkgKwogIGdlb21fc2YoZGF0YSA9IGhpZ2h3YXlzJG9zbV9saW5lcywgY29sb3IgPSAiZ3JheTMwIiwgc2l6ZSA9IDAuMywgYWxwaGEgPSAwLjUpICsKICBnZW9tX3NmKGRhdGEgPSByaXZlcnMkb3NtX3BvbHlnb25zLCBjb2xvciA9ICJsaWdodGJsdWUiLCBhbHBoYSA9IDAuNSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IGRhdDIsIGFlcyh4ID0gZnJvbV9sb25naXR1ZGUsIHkgPSBmcm9tX2xhdGl0dWRlKSwgCiAgICAgICAgICAgICBjb2xvciA9ICJibHVlIiwgYWxwaGEgPSAwLjcsIHNpemUgPSAwLjMpICsKICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICIjRDJGQkQ0IiwgaGlnaCA9ICIjNTI3RDgyIiwgbmEudmFsdWUgPSAidHJhbnNwYXJlbnQiKSArCiAgbGFicyh0aXRsZSA9ICJUcmFjdHMgYnkgTWVkaWFuIEluY29tZSIsIHN1YnRpdGxlID0gIjIwMjIgfCBDaGF0dGFub29nYSwgVE4iKSArCiAgeWxpbShtaW4oZGF0X2NlbnN1cyRmcm9tX2xhdGl0dWRlKSwgbWF4KGRhdF9jZW5zdXMkZnJvbV9sYXRpdHVkZSkpKwogIHhsaW0obWluKGRhdF9jZW5zdXMkZnJvbV9sb25naXR1ZGUpLCBtYXgoZGF0X2NlbnN1cyRmcm9tX2xvbmdpdHVkZSkpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgbWFwVGhlbWUKCgpwbG90MyA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhID0gY2hhdFRyYWN0cyAlPiUgc3RfdHJhbnNmb3JtKGNycyA9IDQzMjYpLCBmaWxsID0gImRhcmtncmF5IiwgY29sb3IgPSAiZ3JheTkwIikgKwogIGdlb21fc2YoZGF0YSA9IGludGVyc2VjdGVkX2NoYXRDZW5zdXMgJT4lIG11dGF0ZShQZXJjZW50X05vbl9XaGl0ZSA9IDEgLSBQZXJjZW50X1doaXRlKSwgCiAgICAgICAgICBhZXMoZmlsbCA9IFBlcmNlbnRfTm9uX1doaXRlKSwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMC4wNSkgKwogIGdlb21fc2YoZGF0YSA9IGhpZ2h3YXlzJG9zbV9saW5lcywgY29sb3IgPSAiZ3JheTMwIiwgc2l6ZSA9IDAuMywgYWxwaGEgPSAwLjUpICsKICBnZW9tX3NmKGRhdGEgPSByaXZlcnMkb3NtX3BvbHlnb25zLCBjb2xvciA9ICJsaWdodGJsdWUiLCBhbHBoYSA9IDAuNSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IGRhdDIsIGFlcyh4ID0gZnJvbV9sb25naXR1ZGUsIHkgPSBmcm9tX2xhdGl0dWRlKSwgCiAgICAgICAgICAgICBjb2xvciA9ICJibHVlIiwgYWxwaGEgPSAwLjcsIHNpemUgPSAwLjMpICsKICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICIjRDJGQkQ0IiwgaGlnaCA9ICIjNTI3RDgyIiwgbmEudmFsdWUgPSAidHJhbnNwYXJlbnQiKSArCiAgbGFicyh0aXRsZSA9ICJUcmFjdHMgYnkgUGVyY2VudCBOb24tV2hpdGUiLCBzdWJ0aXRsZSA9ICIyMDIyIHwgQ2hhdHRhbm9vZ2EsIFROIikgKwogIHlsaW0obWluKGRhdF9jZW5zdXMkZnJvbV9sYXRpdHVkZSksIG1heChkYXRfY2Vuc3VzJGZyb21fbGF0aXR1ZGUpKSsKICB4bGltKG1pbihkYXRfY2Vuc3VzJGZyb21fbG9uZ2l0dWRlKSwgbWF4KGRhdF9jZW5zdXMkZnJvbV9sb25naXR1ZGUpKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKwogIG1hcFRoZW1lCgojICMgIyBpbnN0YWxsLnBhY2thZ2VzKCJwYXRjaHdvcmsiKQojIGxpYnJhcnkocGF0Y2h3b3JrKQojIHBsb3QxICsgcGxvdDIgKyBwbG90MwpgYGAKCiMjIEZlYXR1cmUgRW5naW5lZXJpbmcKClRvIGJ1aWxkIG91ciBtb2RlbCwgd2UgY29uZHVjdCBmZWF0dXJlIGVuZ2luZWVyaW5nLCBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzLCBhbmQgY29ycmVsYXRpb24gYW5hbHlzaXMuIFRoZSBwcmltYXJ5IGVuZ2luZWVyZWQgZmVhdHVyZSBhcmUgdGVtcG9yYWwgbGFncyB3aGljaCByZXByZXNlbnQgdGhlIG51bWJlciBvZiB0cmlwcyBpbiB0aGUgcHJldmlvdXMgaG91cnMuIFdlIGZpbmQgdGhhdCB0aGUgbGFncyBhcmUgc3Ryb25nbHkgY29ycmVsYXRlZCB3aXRoIG91ciBkZXBlbmRlbnQgdmFyaWFibGUsIFRyaXBfQ291bnQsIHdoaWNoIHByb3ZpZGVzIHN0cm9uZyBpbnNpZ2h0IGludG8gaG93IHRvIHJlLWJhbGFuY2UgdGhlIEJpa2UgQ2hhdHRhbm9vZ2EgZmxlZXQuCgojIyMgVGltZS1TcGFjZSBQYW5lbAoKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIHJlc3VsdHM9J2hpZGUnfQpsZW5ndGgodW5pcXVlKGRhdF9jZW5zdXMkaW50ZXJ2YWw2MCkpICogbGVuZ3RoKHVuaXF1ZShkYXRfY2Vuc3VzJFN0YXJ0LlN0YXRpb24uSUQpKSAjNjk3MjAgY29tYmluYXRpb25zCgpzdHVkeS5wYW5lbCA8LSAKICBleHBhbmQuZ3JpZChpbnRlcnZhbDYwPXVuaXF1ZShkYXRfY2Vuc3VzJGludGVydmFsNjApLCAjZ29pbmcgdG8gcG9wdWxhdGUgY29tYmluYXRpb25zIAogICAgICAgICAgICAgIFN0YXJ0LlN0YXRpb24uSUQgPSB1bmlxdWUoZGF0X2NlbnN1cyRTdGFydC5TdGF0aW9uLklEKSkgJT4lICNzdW1tYXJpemUgYnkgaG91ciBhbmQgc3RhdGlvbgogIGxlZnRfam9pbiguLCBkYXRfY2Vuc3VzICU+JSAjam9pbnMgYnkgc3RhdGlvbl9pZCBzaW5jZSBpdHMgdGhlIG9ubHkgdW5pcXVlIHZhcmlhYmxlIAogICAgICAgICAgICAgIHNlbGVjdChTdGFydC5TdGF0aW9uLklELCBTdGFydC5TdGF0aW9uLk5hbWUsIE9yaWdpbi5UcmFjdCwgZnJvbV9sb25naXR1ZGUsIGZyb21fbGF0aXR1ZGUgKSU+JQogICAgICAgICAgICAgIGRpc3RpbmN0KCkgJT4lCiAgICAgICAgICAgICAgZ3JvdXBfYnkoU3RhcnQuU3RhdGlvbi5JRCkgJT4lCiAgICAgICAgICAgICAgc2xpY2UoMSkpCgpyaWRlLnBhbmVsIDwtIAogIGRhdF9jZW5zdXMgJT4lCiAgbXV0YXRlKFRyaXBfQ291bnRlciA9IDEpICU+JQogIHJpZ2h0X2pvaW4oc3R1ZHkucGFuZWwpICU+JSAKICBncm91cF9ieShpbnRlcnZhbDYwLCBTdGFydC5TdGF0aW9uLklELCBTdGFydC5TdGF0aW9uLk5hbWUsIE9yaWdpbi5UcmFjdCwgZnJvbV9sb25naXR1ZGUsIGZyb21fbGF0aXR1ZGUpICU+JQogIHN1bW1hcml6ZShUcmlwX0NvdW50ID0gc3VtKFRyaXBfQ291bnRlciwgbmEucm09VCkpICU+JQogIGxlZnRfam9pbih3ZWF0aGVyLlBhbmVsKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZmlsdGVyKGlzLm5hKFN0YXJ0LlN0YXRpb24uSUQpID09IEZBTFNFKSAlPiUKICBtdXRhdGUod2VlayA9IHdlZWsoaW50ZXJ2YWw2MCksCiAgICAgICAgIGRvdHcgPSB3ZGF5KGludGVydmFsNjAsIGxhYmVsID0gVFJVRSkpICU+JQogIGZpbHRlcihpcy5uYShPcmlnaW4uVHJhY3QpID09IEZBTFNFKQoKcmlkZS5wYW5lbCA8LSAKICBsZWZ0X2pvaW4ocmlkZS5wYW5lbCwgY2hhdENlbnN1cyAlPiUKICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgICAgICAgICAgICAgc2VsZWN0KC1nZW9tZXRyeSksIGJ5ID0gYygiT3JpZ2luLlRyYWN0IiA9ICJHRU9JRCIpKQoKIyMgQ3JlYXRlIFRpbWUgTG9ncwoKI2luc3RhbGwucGFja2FnZXMoInNwYXRpYWxyZWciKQpsaWJyYXJ5KHNwYXRpYWxyZWcpCgpyaWRlLnBhbmVsIDwtIAogIHJpZGUucGFuZWwgJT4lIAogIGFycmFuZ2UoU3RhcnQuU3RhdGlvbi5JRCwgaW50ZXJ2YWw2MCkgJT4lICAKICBtdXRhdGUobGFnSG91ciA9IGRwbHlyOjpsYWcoVHJpcF9Db3VudCwxKSwgI2Zyb20gYW4gaG91ciBhZ28KICAgICAgICAgbGFnMkhvdXJzID0gZHBseXI6OmxhZyhUcmlwX0NvdW50LDIpLCAjZnJvbSB0d28gaG91cnMgYWdvCiAgICAgICAgIGxhZzNIb3VycyA9IGRwbHlyOjpsYWcoVHJpcF9Db3VudCwzKSwgI2Zyb20gdGhyZWUgaG91cnMgYWdvLCBldGMuIAogICAgICAgICBsYWc0SG91cnMgPSBkcGx5cjo6bGFnKFRyaXBfQ291bnQsNCksCiAgICAgICAgIGxhZzEySG91cnMgPSBkcGx5cjo6bGFnKFRyaXBfQ291bnQsMTIpLAogICAgICAgICBsYWcxZGF5ID0gZHBseXI6OmxhZyhUcmlwX0NvdW50LDI0KSwKICAgICAgICAgaG9saWRheSA9IGlmZWxzZSh5ZGF5KGludGVydmFsNjApID09IDE0OCwxLDApKSAlPiUgI01lbW9yaWFsIGRheSAtIGJ1dCB3aGVuIEkgY2hhbmdlIHRvIE1hcmF0aG9uIHdlZWtlbmQsIERheSA2NSwgaXQgYnJlYWtzIC0gbm90IHN1cmUgd2h5CiAgIG11dGF0ZShkYXkgPSB5ZGF5KGludGVydmFsNjApKSAlPiUKICAgbXV0YXRlKGhvbGlkYXlMYWcgPSBjYXNlX3doZW4oZHBseXI6OmxhZyhob2xpZGF5LCAxKSA9PSAxIH4gIlBsdXNPbmVEYXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6bGFnKGhvbGlkYXksIDIpID09IDEgfiAiUGx1c1R3b0RheXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6bGFnKGhvbGlkYXksIDMpID09IDEgfiAiUGx1c1RocmVlRGF5cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjpsZWFkKGhvbGlkYXksIDEpID09IDEgfiAiTWludXNPbmVEYXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6bGVhZChob2xpZGF5LCAyKSA9PSAxIH4gIk1pbnVzVHdvRGF5cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjpsZWFkKGhvbGlkYXksIDMpID09IDEgfiAiTWludXNUaHJlZURheXMiKSwKICAgICAgICAgaG9saWRheUxhZyA9IGlmZWxzZShpcy5uYShob2xpZGF5TGFnKSA9PSBUUlVFLCAwLCBob2xpZGF5TGFnKSkgCgoKI2xvb2sgYXQgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIGxhZ3MKYXMuZGF0YS5mcmFtZShyaWRlLnBhbmVsKSAlPiUKICAgIGdyb3VwX2J5KGludGVydmFsNjApICU+JSAKICAgIHN1bW1hcmlzZV9hdCh2YXJzKHN0YXJ0c193aXRoKCJsYWciKSwgIlRyaXBfQ291bnQiKSwgbWVhbiwgbmEucm0gPSBUUlVFKSAlPiUKICAgIGdhdGhlcihWYXJpYWJsZSwgVmFsdWUsIC1pbnRlcnZhbDYwLCAtVHJpcF9Db3VudCkgJT4lCiAgICBtdXRhdGUoVmFyaWFibGUgPSBmYWN0b3IoVmFyaWFibGUsIGxldmVscz1jKCJsYWdIb3VyIiwibGFnMkhvdXJzIiwibGFnM0hvdXJzIiwibGFnNEhvdXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxhZzEySG91cnMiLCJsYWcxZGF5IikpKSU+JQogICAgZ3JvdXBfYnkoVmFyaWFibGUpICU+JSAgCiAgICBzdW1tYXJpemUoY29ycmVsYXRpb24gPSByb3VuZChjb3IoVmFsdWUsIFRyaXBfQ291bnQpLDIpKSAlPiUKICAgIGtibChjYXB0aW9uID0gIkNvcnJlbGF0aW9ucyBiZXR3ZWVuIEVuZ2luZWVyZWQgRmVhdHVyZXMgYW5kIERlcGVuZGVudCBWYXJpYWJsZSIpICU+JQogICAga2FibGVfcGFwZXIoYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIsIGZ1bGxfd2lkdGggPSBUUlVFKQpgYGAKClRvIHByb3ZpZGUgYSBtb3JlIGludHVpdGl2ZSB2aXN1YWwgcmVwcmVzZW50YXRpb24gb2YgdGhlIGJpa2Ugc2hhcmUgdHJpcHMsIHdlIHBsb3QgYW4gYW5pbWF0ZWQgbWFwIHdpdGggdGVtcG9yYWwgYW5kIHNwYXRpYWwgY29udGV4dC4gQWRkaXRpb25hbGx5LCB3ZSBvYnNlcnZlIHRoYXQgc3RhdGlvbnMgYXJlIGN1cnJlbnRseSBsb2NhdGVkIGluIHdlYWx0aGllciBuZWlnaGJvcmhvb2RzIHJhdGhlciB0aGFuIHRoZSBtb3N0IHBvcHVsb3VzIGFyZWFzLgoKYGBge3IgYW5pbWF0ZWQgbWFwLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0Kc3RhdGlvbnM8LQogIGRhdDIgJT4lIAogIGRwbHlyOjpzZWxlY3QoU3RhcnQuU3RhdGlvbi5JRCwgZnJvbV9sb25naXR1ZGUsIGZyb21fbGF0aXR1ZGUpICU+JQogIGdyb3VwX2J5KFN0YXJ0LlN0YXRpb24uSUQpICU+JQogIGRpc3RpbmN0KCklPiUKICBmaWx0ZXIoaXMubmEoZnJvbV9sb25naXR1ZGUpID09IEZBTFNFICYKICAgICAgICAgICBpcy5uYShmcm9tX2xhdGl0dWRlKSA9PSBGQUxTRSkgJT4lCiAgc3RfYXNfc2YoLiwgY29vcmRzID0gYygiZnJvbV9sb25naXR1ZGUiLCAiZnJvbV9sYXRpdHVkZSIpLCBjcnMgPSA0MzI2KQoKCm9uZV9kYXkgPC0KICBkYXRfY2Vuc3VzICU+JQogIGZpbHRlcih3ZWVrID09IDE0ICYgZG90dyA9PSAiVGh1IikKCm9uZV9kYXkucGFuZWwgPC0KICBleHBhbmQuZ3JpZCgKICAgIGludGVydmFsNjAgPSB1bmlxdWUob25lX2RheSRpbnRlcnZhbDYwKSwKICAgIE9yaWdpbi5UcmFjdCA9IHVuaXF1ZShyaWRlLnBhbmVsJE9yaWdpbi5UcmFjdCkpCgoKYW5pbWF0aW9uX2RhdGEgPC0KICBtdXRhdGUob25lX2RheSwgVHJpcF9Db3VudGVyID0gMSkgJT4lCiAgcmlnaHRfam9pbihvbmVfZGF5KSAlPiUgCiAgZ3JvdXBfYnkoaW50ZXJ2YWw2MCwgU3RhcnQuU3RhdGlvbi5JRCkgJT4lCiAgc3VtbWFyaXNlKFRyaXBfQ291bnQgPSBzdW0oVHJpcF9Db3VudGVyLCBuYS5ybT1UKSkgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgcmlnaHRfam9pbihzdGF0aW9ucywgYnk9YygiU3RhcnQuU3RhdGlvbi5JRCIgPSAiU3RhcnQuU3RhdGlvbi5JRCIpKSAlPiUgCiAgc3Rfc2YoKSAlPiUKICBtdXRhdGUoVHJpcHMgPSBjYXNlX3doZW4oVHJpcF9Db3VudCA9PSAwIH4gIjAgdHJpcHMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBUcmlwX0NvdW50ID09IDEgfiAiMSB0cmlwcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyaXBfQ291bnQgPT0gMiB+ICIyIHRyaXBzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJpcF9Db3VudCA+IDIgJiBUcmlwX0NvdW50IDw9IDQgfiAiMy00IHRyaXBzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJpcF9Db3VudCA+IDQgfiAiNSsgdHJpcHMiKSkgJT4lCiAgbXV0YXRlKFRyaXBzICA9IGZjdF9yZWxldmVsKFRyaXBzLCAiMCB0cmlwcyIsIjEgdHJpcHMiLCIyIHRyaXBzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjMtNCB0cmlwcyIsIjUrIHRyaXBzIikpCgoKYmlrZV9hbmltYXRpb24gPC0KICBnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhID0gY2hhdENlbnN1cyAlPiUKICAgICAgICAgICAgc3RfdHJhbnNmb3JtKGNycz00MzI2KSkrCiAgZ2VvbV9zZihkYXRhID0gY2hhdENlbnN1cywgYWVzKGZpbGwgPSBNZWRfSW5jKSwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMC4wNSkgKwogIGdlb21fc2YoZGF0YSA9IGFuaW1hdGlvbl9kYXRhLCAKICAgICAgICAgICAgIGFlcyhjb2xvciA9IFRyaXBzKSwgc2l6ZSA9IDIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTQpICsKICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJncmF5OTAiLCBoaWdoID0gImdyYXkzMCIsIG5hLnZhbHVlID0gIndoaXRlIikgKyAgIyBDaGFuZ2UgdGhlIGNvbG9yIHBhbGV0dGUKIHlsaW0obWluKGRhdF9jZW5zdXMkZnJvbV9sYXRpdHVkZSksIG1heChkYXRfY2Vuc3VzJGZyb21fbGF0aXR1ZGUpKSsKIHhsaW0obWluKGRhdF9jZW5zdXMkZnJvbV9sb25naXR1ZGUpLCBtYXgoZGF0X2NlbnN1cyRmcm9tX2xvbmdpdHVkZSkpKwogIGxhYnModGl0bGUgPSAiQmlrZSBUcmlwcyBieSBTdGFydCBTdGF0aW9ucyBmb3IgdGhlIGZpcnN0IHdlZWsgb2YgQXByaWwsIDIwMjIiLAogICAgICAgc3VidGl0bGUgPSAiNjAtbWludXRlIGludGVydmFsczoge2N1cnJlbnRfZnJhbWV9IikgKwogIHRyYW5zaXRpb25fbWFudWFsKGludGVydmFsNjApICsKICBtYXBUaGVtZQoKYW5pbWF0ZShiaWtlX2FuaW1hdGlvbiwgZHVyYXRpb249MTAsIHJlbmRlcmVyID0gZ2lmc2tpX3JlbmRlcmVyKCkpCmBgYAoKIyMgU3BhdGlvdGVtcG9yYWwgUHJlZGljdGlvbgoKVG8gY29uZHVjdCByZWdyZXNzaW9uIGFuYWx5c2lzIGFuZCBwcmVkaWN0aW9uIG9uIGJpa2UgcmlkZSBkYXRhLCB0aGUgZGF0YSBpcyBzcGxpdCBpbnRvIHRyYWluaW5nIChyaWRlLlRyYWluKSBhbmQgdGVzdGluZyAocmlkZS5UZXN0KSBzZXRzIGJhc2VkIG9uIHRoZSB3ZWVrIHZhcmlhYmxlLiBTZXZlcmFsIGxpbmVhciByZWdyZXNzaW9uIG1vZGVscyAocmVnMSB0byByZWc2KSBhcmUgZml0dGVkIHVzaW5nIHRoZSByaWRlLlRyYWluIGRhdGEgYW5kIGRpZmZlcmVudCBjb21iaW5hdGlvbnMgb2YgcHJlZGljdG9yIHZhcmlhYmxlcywgaW5jbHVkaW5nIGhvdXIgb2YgdGhlIGRheSwgZGF5IG9mIHRoZSB3ZWVrLCB0ZW1wZXJhdHVyZSwgcHJlY2lwaXRhdGlvbiwgbGFncyBvZiB0cmlwIGNvdW50cywgaG9saWRheSBpbmZvcm1hdGlvbiwgYW5kIGRlbW9ncmFwaGljIHZhcmlhYmxlcy4KCldoZW4gY29tcGFyaW5nIE1vZGVsIDMgKFN0YXJ0LlN0YXRpb24uTmFtZSArIGhvdXIoaW50ZXJ2YWw2MCkgKyBkb3R3ICsgVGVtcGVyYXR1cmUgKyBQcmVjaXBpdGF0aW9uKSBhbmQgTW9kZWwgNiAoTW9kZWwgMyArIGxhZ0hvdXIgKyBsYWcySG91cnMgK2xhZzNIb3VycyArbGFnMTJIb3VycyArIGxhZzFkYXkgKyBob2xpZGF5TGFnICsgaG9saWRheSArIFRvdGFsX1BvcCArIE1lZF9JbmMgKyBQZXJjZW50X1Rha2luZ19QdWJsaWNfVHJhbnMpLCBNb2RlbCA2IGhhcyBhIGhpZ2hlciBSwrIgKDAuMjI3KSBjb21wYXJlZCB0byBNb2RlbCAzICgwLjE3MSkgd2hpY2ggc3VnZ2VzdHMgdGhhdCBNb2RlbCA2IGV4cGxhaW5zIGEgbGFyZ2VyIHByb3BvcnRpb24gb2YgdGhlIHZhcmlhbmNlIGluIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgY29tcGFyZWQgdG8gTW9kZWwgMy4gQWRkaXRpb25hbGx5LCBNb2RlbCA2IGhhcyBhIHNsaWdodGx5IGxvd2VyIHJlc2lkdWFsIHN0YW5kYXJkIGVycm9yICgxLjIwMSkgY29tcGFyZWQgdG8gTW9kZWwgMyAoMS4yNDMpIHdoaWNoIGluZGljYXRlcyBhIGJldHRlciBmaXQgb2YgdGhlIG1vZGVsIHRvIHRoZSBkYXRhLiBMYXN0bHksIHdoaWxlIGJvdGggbW9kZWxzIGhhdmUgYSBzaWduaWZpY2FudCBGIHN0YXRpc3RpYywgTW9kZWwgNiAoRiA9IDYzLjgyMikgaGFzIGEgaGlnaGVyIHZhbHVlIGNvbXBhcmVkIHRvIE1vZGVsIDMgKEYgPSA1NS44ODgpLgoKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIHJlc3VsdHM9J2hpZGUnfQojc3BsaXRzIGRhdGEgYWJvdXQgaW4gaGFsZiAKcmlkZS5UcmFpbiA8LSBmaWx0ZXIocmlkZS5wYW5lbCwgd2VlayA+PSAyMCkKcmlkZS5UZXN0IDwtIGZpbHRlcihyaWRlLnBhbmVsLCB3ZWVrIDwgMjApCgpyZWcxIDwtIAogIGxtKFRyaXBfQ291bnQgfiAgaG91cihpbnRlcnZhbDYwKSArIGRvdHcgKyBUZW1wZXJhdHVyZSwgIGRhdGE9cmlkZS5UcmFpbikKCnJlZzIgPC0gCiAgbG0oVHJpcF9Db3VudCB+ICBTdGFydC5TdGF0aW9uLk5hbWUgKyBkb3R3ICsgVGVtcGVyYXR1cmUsICBkYXRhPXJpZGUuVHJhaW4pCgpyZWczIDwtIAogIGxtKFRyaXBfQ291bnQgfiAgU3RhcnQuU3RhdGlvbi5OYW1lICsgaG91cihpbnRlcnZhbDYwKSArIGRvdHcgKyBUZW1wZXJhdHVyZSArIFByZWNpcGl0YXRpb24sIAogICAgIGRhdGE9cmlkZS5UcmFpbikKCnJlZzQgPC0gCiAgbG0oVHJpcF9Db3VudCB+ICBTdGFydC5TdGF0aW9uLk5hbWUgKyAgaG91cihpbnRlcnZhbDYwKSArIGRvdHcgKyBUZW1wZXJhdHVyZSArIFByZWNpcGl0YXRpb24gKwogICAgICAgICAgICAgICAgICAgbGFnSG91ciArIGxhZzJIb3VycyArbGFnM0hvdXJzICsgbGFnMTJIb3VycyArIGxhZzFkYXksIAogICAgIGRhdGE9cmlkZS5UcmFpbikKCnJlZzUgPC0gCiAgbG0oVHJpcF9Db3VudCB+ICBTdGFydC5TdGF0aW9uLk5hbWUgKyBob3VyKGludGVydmFsNjApICsgZG90dyArIFRlbXBlcmF0dXJlICsgUHJlY2lwaXRhdGlvbiArCiAgICAgICAgICAgICAgICAgICBsYWdIb3VyICsgbGFnMkhvdXJzICtsYWczSG91cnMgK2xhZzEySG91cnMgKyBsYWcxZGF5ICsgaG9saWRheUxhZyArIGhvbGlkYXksIAogICAgIGRhdGE9cmlkZS5UcmFpbikKCnJlZzYgPC0KICBsbShUcmlwX0NvdW50IH4gIFN0YXJ0LlN0YXRpb24uTmFtZSArIGhvdXIoaW50ZXJ2YWw2MCkgKyBkb3R3ICsgVGVtcGVyYXR1cmUgKyBQcmVjaXBpdGF0aW9uICsKICAgICAgICAgICAgICAgICAgIGxhZ0hvdXIgKyBsYWcySG91cnMgK2xhZzNIb3VycyArbGFnMTJIb3VycyArIGxhZzFkYXkgKyBob2xpZGF5TGFnICsgaG9saWRheSArIAogICAgICAgICAgICAgICAgICAgVG90YWxfUG9wICsgTWVkX0luYyArIFBlcmNlbnRfVGFraW5nX1B1YmxpY19UcmFucywgCiAgICAgZGF0YT1yaWRlLlRyYWluKQoKYGBgCgpgYGB7ciBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgcmVzdWx0cz0naGlkZSd9CmxpYnJhcnkoc3RhcmdhemVyKQpzdGFyZ2F6ZXIocmVnMywgcmVnNiwgdHlwZT0idGV4dCIpIApgYGAgCgpXZSBkZWZpbmVkIGEgZnVuY3Rpb24gdG8gcHJlZGljdCB0cmlwIGNvdW50cyBmb3IgYSBnaXZlbiBtb2RlbCBhbmQgdGVzdCBkYXRhc2V0LiBGb3IgZWFjaCBtb2RlbCAocmVnMSB0byByZWc2KSwgcHJlZGljdGlvbnMgYXJlIGdlbmVyYXRlZCBhbG9uZyB3aXRoIG9ic2VydmVkIHZhbHVlcywgYXJlIGdhdGhlcmVkIGludG8gYSBsb25nIGZvcm1hdCwgYW5kIGFkZGl0aW9uYWwgbWV0cmljcyBsaWtlIEFic29sdXRlIEVycm9yIChBRSksIE1lYW4gQWJzb2x1dGUgRXJyb3IgKE1BRSksIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gb2YgQUUuCgpgYGB7ciBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgcmVzdWx0cz0naGlkZSd9CgojUHJlZGljdCBmb3IgdGVzdCBkYXRhIApyaWRlLlRlc3Qud2Vla05lc3QgPC0gCiAgcmlkZS5UZXN0ICU+JQogIG5lc3QoLXdlZWspIAoKbW9kZWxfcHJlZCA8LSBmdW5jdGlvbihkYXQsIGZpdCl7CiAgIHByZWQgPC0gcHJlZGljdChmaXQsIG5ld2RhdGEgPSBkYXQpfSAjcHJlZGljdCBmb3IgZml0IHVzaW5nIHRoZSBkYXQgZGF0YWZyYW1lIAoKd2Vla19wcmVkaWN0aW9ucyA8LSAKICByaWRlLlRlc3Qud2Vla05lc3QgJT4lIAogICAgbXV0YXRlKEFUaW1lX0ZFID0gbWFwKC54ID0gZGF0YSwgZml0ID0gcmVnMSwgLmYgPSBtb2RlbF9wcmVkKSwgI21hcHBpbmcgdGhlIGZ1bmN0aW9uIGZvciBkaWZmZXJlbnQgbW9kZWxzIGQKICAgICAgICAgICBCU3BhY2VfRkUgPSBtYXAoLnggPSBkYXRhLCBmaXQgPSByZWcyLCAuZiA9IG1vZGVsX3ByZWQpLAogICAgICAgICAgIENUaW1lX1NwYWNlX0ZFID0gbWFwKC54ID0gZGF0YSwgZml0ID0gcmVnMywgLmYgPSBtb2RlbF9wcmVkKSwKICAgICAgICAgICBEVGltZV9TcGFjZV9GRV90aW1lTGFncyA9IG1hcCgueCA9IGRhdGEsIGZpdCA9IHJlZzQsIC5mID0gbW9kZWxfcHJlZCksCiAgICAgICAgICAgRVRpbWVfU3BhY2VfRkVfdGltZUxhZ3NfaG9saWRheUxhZ3MgPSBtYXAoLnggPSBkYXRhLCBmaXQgPSByZWc1LCAuZiA9IG1vZGVsX3ByZWQpLAogICAgICAgICAgIEZUaW1lX1NwYWNlX0ZFX3RpbWVMYWdzX2hvbGlkYXlMYWdzX2RlbW8gPSBtYXAoLnggPSBkYXRhLCBmaXQgPSByZWc2LCAuZiA9IG1vZGVsX3ByZWQpKSAlPiUgCiAgICBnYXRoZXIoUmVncmVzc2lvbiwgUHJlZGljdGlvbiwgLWRhdGEsIC13ZWVrKSAlPiUKICAgIG11dGF0ZShPYnNlcnZlZCA9IG1hcChkYXRhLCBwdWxsLCBUcmlwX0NvdW50KSwKICAgICAgICAgICBBYnNvbHV0ZV9FcnJvciA9IG1hcDIoT2JzZXJ2ZWQsIFByZWRpY3Rpb24sICB+IGFicygueCAtIC55KSksCiAgICAgICAgICAgTUFFID0gbWFwX2RibChBYnNvbHV0ZV9FcnJvciwgbWVhbiwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICBzZF9BRSA9IG1hcF9kYmwoQWJzb2x1dGVfRXJyb3IsIHNkLCBuYS5ybSA9IFRSVUUpKQoKYGBgCgoKIyAqKkRpc2N1c3Npb24qKgoKIyMgRXhhbWluZSBFcnJvciBNZXRyaWNzIGZvciBBY2N1cmFjeQoKVGhlIHBsb3QgaWxsdXN0cmF0ZXMgdGhlIE1lYW4gQWJzb2x1dGUgRXJyb3JzIChNQUUpIGZvciBkaWZmZXJlbnQgbW9kZWwgc3BlY2lmaWNhdGlvbnMgYWNyb3NzIHdlZWtzLiBOb3RhYmx5LCBNb2RlbHMgNCwgNSwgYW5kIDYgaW5jbHVkZSB0aW1lIGxhZ3MgYXMgYSBmZWF0dXJlIGFuZCBjb25zaXN0ZW50bHkgZGlzcGxheSBsb3dlciBNQUUuIFdoaWxlIE1vZGVsIEQgaGFzIGEgc2xpZ2h0bHkgbG93ZXIgTUFFLCB0aGUgZW5oYW5jZWQgc3RhdGlzdGljYWwgcGVyZm9ybWFuY2Ugb2YgTW9kZWwgRiBtYXkganVzdGlmeSBpdHMgbWFyZ2luYWxseSBoaWdoZXIgTUFFLiAKCmBgYHtyIHBsb3RfZXJyb3JzX2J5X21vZGVsIH0Kd2Vla19wcmVkaWN0aW9ucyAlPiUKICBkcGx5cjo6c2VsZWN0KHdlZWssIFJlZ3Jlc3Npb24sIE1BRSkgJT4lCiAgZ2F0aGVyKFZhcmlhYmxlLCBNQUUsIC1SZWdyZXNzaW9uLCAtd2VlaykgJT4lCiAgZ2dwbG90KGFlcyh3ZWVrLCBNQUUpKSArIAogICAgZ2VvbV9iYXIoYWVzKGZpbGwgPSBSZWdyZXNzaW9uKSwgcG9zaXRpb24gPSAiZG9kZ2UiLCBzdGF0PSJpZGVudGl0eSIpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGU2KSArCiAgICBsYWJzKHRpdGxlID0gIk1lYW4gQWJzb2x1dGUgRXJyb3JzIGJ5IG1vZGVsIHNwZWNpZmljYXRpb24gYW5kIHdlZWsiKSArCiAgcGxvdFRoZW1lCmBgYAoKVGhlIHBsb3QgaWxsdXN0cmF0ZXMgdGhlIHByZWRpY3RlZCBhbmQgb2JzZXJ2ZWQgdGltZSBzZXJpZXMgb2YgYmlrZSBzaGFyZSB0cmlwcyBmb3IgZGlmZmVyZW50IG1vZGVsIHNwZWNpZmljYXRpb25zIGFjcm9zcyBtdWx0aXBsZSB3ZWVrcy4gVGhlIGRpc2NyZXBhbmN5IGJldHdlZW4gb2JzZXJ2ZWQgYW5kIHByZWRpY3RlZCB2YWx1ZXMsIHBhcnRpY3VsYXJseSBhcm91bmQgcGVhayB0aW1lcywgbWF5IGFyaXNlIGZyb20gdGhlIGluaGVyZW50IGNvbXBsZXhpdHkgb2YgcmVhbC13b3JsZCBiaWtlIHNoYXJlIHRyaXAgcGF0dGVybnMuIEZhY3RvcnMgc3VjaCBhcyBleHRlcm5hbCBldmVudHMsIHVuZm9yZXNlZW4gZmx1Y3R1YXRpb25zIGluIGRlbWFuZCwgb3IgdW5hY2NvdW50ZWQgdmFyaWFibGVzIGluIHRoZSBtb2RlbHMgbWF5IGNvbnRyaWJ1dGUgdG8gdGhlIGRpZmZlcmVuY2VzLiBHZW5lcmFsbHksIHRoaXMgY29tcGFyYXRpdmUgYW5hbHlzaXMgc2hvd3MgaG93IHdlbGwgZWFjaCBtb2RlbCBhbGlnbnMgd2l0aCB0aGUgb2JzZXJ2ZWQgZGF0YSwgcHJvdmlkaW5nIGluc2lnaHRzIGludG8gdGhlIHByZWRpY3RpdmUgcGVyZm9ybWFuY2Ugb2YgZGlmZmVyZW50IG1vZGVsIHNwZWNpZmljYXRpb25zLgoKYGBge3IgZXJyb3JfdnNfYWN0dWFsX3RpbWVzZXJpZXMgLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0Kd2Vla19wcmVkaWN0aW9ucyAlPiUgCiAgICBtdXRhdGUoaW50ZXJ2YWw2MCA9IG1hcChkYXRhLCBwdWxsLCBpbnRlcnZhbDYwKSwKICAgICAgICAgICBTdGFydC5TdGF0aW9uLklEID0gbWFwKGRhdGEsIHB1bGwsIFN0YXJ0LlN0YXRpb24uSUQpKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoaW50ZXJ2YWw2MCwgU3RhcnQuU3RhdGlvbi5JRCwgT2JzZXJ2ZWQsIFByZWRpY3Rpb24sIFJlZ3Jlc3Npb24pICU+JQogICAgdW5uZXN0KCkgJT4lCiAgICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtUmVncmVzc2lvbiwgLWludGVydmFsNjAsIC1TdGFydC5TdGF0aW9uLklEKSAlPiUKICAgIGdyb3VwX2J5KFJlZ3Jlc3Npb24sIFZhcmlhYmxlLCBpbnRlcnZhbDYwKSAlPiUKICAgIHN1bW1hcml6ZShWYWx1ZSA9IHN1bShWYWx1ZSkpICU+JQogICAgZ2dwbG90KGFlcyhpbnRlcnZhbDYwLCBWYWx1ZSwgY29sb3VyPVZhcmlhYmxlKSkgKyAKICAgICAgZ2VvbV9saW5lKHNpemUgPSAxLjEpICsgCiAgICAgIGZhY2V0X3dyYXAoflJlZ3Jlc3Npb24sIG5jb2w9MSkgKwogICAgICBsYWJzKHRpdGxlID0gIlByZWRpY3RlZC9PYnNlcnZlZCBiaWtlIHNoYXJlIHRpbWUgc2VyaWVzIiwgc3VidGl0bGUgPSAiTWFyY2ggLSBNYXkgMjAyMiB8IENoYXR0YW5vb2dhIHwgQSB0ZXN0IHNldCBvZiAyIHdlZWtzIiwgIHggPSAiSG91ciIsIHk9ICJTdGF0aW9uIFRyaXBzIikgKwogICAgICBwbG90VGhlbWUKYGBgCgpTcGF0aWFsbHksIHdlIG9ic2VydmUgdGhhdCB0aGVyZSBpcyBoaWdoZXIgTUFFIGF0IHN0YXRpb25zIHByb3hpbWF0ZSB0byB0aGUgVGVubmVzc2VlIFJpdmVyLgoKPGRpdiBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXIiPgo8aW1nIHNyYz0iL1VzZXJzL0xhdXJhRnJhbmNlc18xL0xpYnJhcnkvQ2xvdWRTdG9yYWdlL0dvb2dsZURyaXZlLWxhdXJhbXVlbGxlcnNvcHBhcnRAZ21haWwuY29tL015IERyaXZlL1Blbm4vQ291cnNlcy9NVVNBNTA4X1BQQS9QUEFfQXNzaWdubWVudDUvQmlrZVNoYXJlL21hcHZpZXcgbWFwLnBuZyIgYWx0PSJBbHQgdGV4dCIgd2lkdGg9Ijc1JSIgYWxpZ24gPSAndic+CjwvZGl2PgoKYGBge3IgZXJyb3JzX2J5X3N0YXRpb24sIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBldmFsPUZBTFNFIH0Kd2Vla19wcmVkaWN0aW9ucyAlPiUgCiAgICBtdXRhdGUoaW50ZXJ2YWw2MCA9IG1hcChkYXRhLCBwdWxsLCBpbnRlcnZhbDYwKSwKICAgICAgICAgICBTdGFydC5TdGF0aW9uLklEID0gbWFwKGRhdGEsIHB1bGwsIFN0YXJ0LlN0YXRpb24uSUQpLCAKICAgICAgICAgICBmcm9tX2xhdGl0dWRlID0gbWFwKGRhdGEsIHB1bGwsIGZyb21fbGF0aXR1ZGUpLCAKICAgICAgICAgICBmcm9tX2xvbmdpdHVkZSA9IG1hcChkYXRhLCBwdWxsLCBmcm9tX2xvbmdpdHVkZSkpICU+JQogICAgc2VsZWN0KGludGVydmFsNjAsIFN0YXJ0LlN0YXRpb24uSUQsIGZyb21fbG9uZ2l0dWRlLCBmcm9tX2xhdGl0dWRlLCBPYnNlcnZlZCwgUHJlZGljdGlvbiwgUmVncmVzc2lvbikgJT4lCiAgICB1bm5lc3QoKSAlPiUKICBmaWx0ZXIoUmVncmVzc2lvbiA9PSAiRlRpbWVfU3BhY2VfRkVfdGltZUxhZ3NfaG9saWRheUxhZ3NfZGVtbyIpICU+JQogIGdyb3VwX2J5KFN0YXJ0LlN0YXRpb24uSUQsIGZyb21fbG9uZ2l0dWRlLCBmcm9tX2xhdGl0dWRlKSAlPiUKICBzdW1tYXJpemUoTUFFID0gbWVhbihhYnMoT2JzZXJ2ZWQtUHJlZGljdGlvbiksIG5hLnJtID0gVFJVRSkpJT4lCmdncGxvdCguKSsKICBnZW9tX3NmKGRhdGEgPSBjaGF0Q2Vuc3VzLCBjb2xvciA9ICJncmV5IiwgZmlsbCA9ICJ0cmFuc3BhcmVudCIpKwogIGdlb21fc2YoZGF0YSA9IGhpZ2h3YXlzJG9zbV9saW5lcywgY29sb3IgPSAiZ3JheTMwIiwgc2l6ZSA9IDAuMywgYWxwaGEgPSAwLjUpICsKICBnZW9tX3NmKGRhdGEgPSByaXZlcnMkb3NtX3BvbHlnb25zLCBjb2xvciA9ICJsaWdodGJsdWUiLCBhbHBoYSA9IDAuNSkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBmcm9tX2xvbmdpdHVkZSwgeSA9IGZyb21fbGF0aXR1ZGUsIGNvbG9yID0gTUFFKSwgCiAgICAgICAgICAgICBmaWxsID0gInRyYW5zcGFyZW50IiwgYWxwaGEgPSAwLjgpKwogIHNjYWxlX2NvbG91cl92aXJpZGlzKGRpcmVjdGlvbiA9IC0xLAogIGRpc2NyZXRlID0gRkFMU0UsIG9wdGlvbiA9ICJEIikrCiAgeWxpbShtaW4oZGF0X2NlbnN1cyRmcm9tX2xhdGl0dWRlKSwgbWF4KGRhdF9jZW5zdXMkZnJvbV9sYXRpdHVkZSkpKwogIHhsaW0obWluKGRhdF9jZW5zdXMkZnJvbV9sb25naXR1ZGUpLCBtYXgoZGF0X2NlbnN1cyRmcm9tX2xvbmdpdHVkZSkpKwogIGxhYnModGl0bGU9Ik1lYW4gQWJzIEVycm9yLCBUZXN0IFNldCwgTW9kZWwgRiIsIAogICAgICAgc3VidGl0bGUgPSAiVGltZV9TcGFjZV9GRV90aW1lTGFnc19ob2xpZGF5TGFnc19kZW1vIikrCiAgbWFwVGhlbWUKCgojIGluc3RhbGwucGFja2FnZXMoIm1hcHZpZXciKQojIGxpYnJhcnkobWFwdmlldykKIyAKIyAjIEFzc3VtaW5nIHlvdSBoYXZlIGFuIHNmIG9iamVjdCBmb3IgY2hhdENlbnN1cwojIGNoYXRDZW5zdXNfc2YgPC0gc3RfYXNfc2YoY2hhdENlbnN1cykKIyAKIyAjIFlvdXIgZXhpc3RpbmcgY29kZQojIHJlc3VsdCA8LSB3ZWVrX3ByZWRpY3Rpb25zICU+JSAKIyAgIG11dGF0ZShpbnRlcnZhbDYwID0gbWFwKGRhdGEsIHB1bGwsIGludGVydmFsNjApLAojICAgICAgICAgIFN0YXJ0LlN0YXRpb24uSUQgPSBtYXAoZGF0YSwgcHVsbCwgU3RhcnQuU3RhdGlvbi5JRCksIAojICAgICAgICAgIGZyb21fbGF0aXR1ZGUgPSBtYXAoZGF0YSwgcHVsbCwgZnJvbV9sYXRpdHVkZSksIAojICAgICAgICAgIGZyb21fbG9uZ2l0dWRlID0gbWFwKGRhdGEsIHB1bGwsIGZyb21fbG9uZ2l0dWRlKSkgJT4lCiMgICBzZWxlY3QoaW50ZXJ2YWw2MCwgU3RhcnQuU3RhdGlvbi5JRCwgZnJvbV9sb25naXR1ZGUsIGZyb21fbGF0aXR1ZGUsIE9ic2VydmVkLCBQcmVkaWN0aW9uLCBSZWdyZXNzaW9uKSAlPiUKIyAgIHVubmVzdCgpICU+JQojICAgZmlsdGVyKFJlZ3Jlc3Npb24gPT0gIkZUaW1lX1NwYWNlX0ZFX3RpbWVMYWdzX2hvbGlkYXlMYWdzX2RlbW8iKSAlPiUKIyAgIGdyb3VwX2J5KFN0YXJ0LlN0YXRpb24uSUQsIGZyb21fbG9uZ2l0dWRlLCBmcm9tX2xhdGl0dWRlKSAlPiUKIyAgIHN1bW1hcml6ZShNQUUgPSBtZWFuKGFicyhPYnNlcnZlZCAtIFByZWRpY3Rpb24pLCBuYS5ybSA9IFRSVUUpKQojIAojICMgQ3JlYXRpbmcgYW4gc2Ygb2JqZWN0IGZvciB0aGUgcmVzdWx0cwojIHJlc3VsdF9zZiA8LSBzdF9hc19zZihyZXN1bHQsIGNvb3JkcyA9IGMoImZyb21fbG9uZ2l0dWRlIiwgImZyb21fbGF0aXR1ZGUiKSkKIyAKIyAjIFBsb3R0aW5nIHdpdGggbWFwdmlldwojIG1hcHZpZXcoY2hhdENlbnN1c19zZikgKwojICAgbWFwdmlldyhoaWdod2F5cyRvc21fbGluZXMsIGNvbC5yZWdpb25zID0gImdyYXkzMCIsIGFscGhhLnJlZ2lvbnMgPSAwLjUpICsKIyAgIG1hcHZpZXcocmVzdWx0X3NmLCB6Y29sID0gIk1BRSIsIGFscGhhID0gMC44LCBsZWdlbmQgPSBUUlVFKQoKYGBgCgojIyBTcGFjZS1UaW1lIEVycm9yIEV2YWx1YXRpb24KCgpUaGUgc2NhdHRlcnBsb3RzIGRlcGljdCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gb2JzZXJ2ZWQgYW5kIHByZWRpY3RlZCBiaWtlIHNoYXJlIHRyaXBzLCB3aXRoIGVhY2ggcG9pbnQgcmVwcmVzZW50aW5nIGEgc3BlY2lmaWMgdGltZSBwZXJpb2QgYW5kIHN0YXRpb24uIFdoaWxlIHRoZSBwbG90cyBzaG93IGEgZ2VuZXJhbGx5IHBvc2l0aXZlIGNvcnJlbGF0aW9uLCB0aGUgbW9kZWxzIHVuZGVyY291bnQgdGhlIG51bWJlciBvZiBwcmVkaWN0ZWQgYmlrZSB0cmlwcy4gCgpgYGB7ciBvYnNfcHJlZF9hbGwsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgY2FjaGU9VFJVRX0Kd2Vla19wcmVkaWN0aW9ucyAlPiUgCiAgICBtdXRhdGUoaW50ZXJ2YWw2MCA9IG1hcChkYXRhLCBwdWxsLCBpbnRlcnZhbDYwKSwKICAgICAgICAgICBTdGFydC5TdGF0aW9uLklEID0gbWFwKGRhdGEsIHB1bGwsIFN0YXJ0LlN0YXRpb24uSUQpLCAKICAgICAgICAgICBmcm9tX2xhdGl0dWRlID0gbWFwKGRhdGEsIHB1bGwsIGZyb21fbGF0aXR1ZGUpLCAKICAgICAgICAgICBmcm9tX2xvbmdpdHVkZSA9IG1hcChkYXRhLCBwdWxsLCBmcm9tX2xvbmdpdHVkZSksCiAgICAgICAgICAgZG90dyA9IG1hcChkYXRhLCBwdWxsLCBkb3R3KSkgJT4lCiAgICBzZWxlY3QoaW50ZXJ2YWw2MCwgU3RhcnQuU3RhdGlvbi5JRCwgZnJvbV9sb25naXR1ZGUsIAogICAgICAgICAgIGZyb21fbGF0aXR1ZGUsIE9ic2VydmVkLCBQcmVkaWN0aW9uLCBSZWdyZXNzaW9uLAogICAgICAgICAgIGRvdHcpICU+JQogICAgdW5uZXN0KCkgJT4lCiAgZmlsdGVyKFJlZ3Jlc3Npb24gPT0gIkVUaW1lX1NwYWNlX0ZFX3RpbWVMYWdzX2hvbGlkYXlMYWdzIiklPiUKICBtdXRhdGUod2Vla2VuZCA9IGlmZWxzZShkb3R3ICVpbiUgYygiU3VuIiwgIlNhdCIpLCAiV2Vla2VuZCIsICJXZWVrZGF5IiksCiAgICAgICAgIHRpbWVfb2ZfZGF5ID0gY2FzZV93aGVuKGhvdXIoaW50ZXJ2YWw2MCkgPCA3IHwgaG91cihpbnRlcnZhbDYwKSA+IDE4IH4gIk92ZXJuaWdodCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvdXIoaW50ZXJ2YWw2MCkgPj0gNyAmIGhvdXIoaW50ZXJ2YWw2MCkgPCAxMCB+ICJBTSBSdXNoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG91cihpbnRlcnZhbDYwKSA+PSAxMCAmIGhvdXIoaW50ZXJ2YWw2MCkgPCAxNSB+ICJNaWQtRGF5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG91cihpbnRlcnZhbDYwKSA+PSAxNSAmIGhvdXIoaW50ZXJ2YWw2MCkgPD0gMTggfiAiUE0gUnVzaCIpKSU+JQogIGdncGxvdCgpKwogIGdlb21fcG9pbnQoYWVzKHg9IE9ic2VydmVkLCB5ID0gUHJlZGljdGlvbikpKwogICAgZ2VvbV9zbW9vdGgoYWVzKHg9IE9ic2VydmVkLCB5PSBQcmVkaWN0aW9uKSwgbWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgY29sb3IgPSAiYmx1ZSIpKwogICAgZ2VvbV9hYmxpbmUoc2xvcGUgPSAxLCBpbnRlcmNlcHQgPSAwKSsKICBmYWNldF9ncmlkKHRpbWVfb2ZfZGF5fndlZWtlbmQpKwogIGxhYnModGl0bGU9Ik9ic2VydmVkIHZzIFByZWRpY3RlZCIsCiAgICAgICB4PSJPYnNlcnZlZCB0cmlwcyIsIAogICAgICAgeT0iUHJlZGljdGVkIHRyaXBzIikrCiAgcGxvdFRoZW1lCmBgYAoKCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgaW5jbHVkZSA9IEZBTFNFfQp3ZWVrX3ByZWRpY3Rpb25zICU+JSAKICAgIG11dGF0ZShpbnRlcnZhbDYwID0gbWFwKGRhdGEsIHB1bGwsIGludGVydmFsNjApLAogICAgICAgICAgIFN0YXJ0LlN0YXRpb24uSUQgPSBtYXAoZGF0YSwgcHVsbCwgU3RhcnQuU3RhdGlvbi5JRCksIAogICAgICAgICAgIGZyb21fbGF0aXR1ZGUgPSBtYXAoZGF0YSwgcHVsbCwgZnJvbV9sYXRpdHVkZSksIAogICAgICAgICAgIGZyb21fbG9uZ2l0dWRlID0gbWFwKGRhdGEsIHB1bGwsIGZyb21fbG9uZ2l0dWRlKSwKICAgICAgICAgICBkb3R3ID0gbWFwKGRhdGEsIHB1bGwsIGRvdHcpICkgJT4lCiAgICBzZWxlY3QoaW50ZXJ2YWw2MCwgU3RhcnQuU3RhdGlvbi5JRCwgZnJvbV9sb25naXR1ZGUsIAogICAgICAgICAgIGZyb21fbGF0aXR1ZGUsIE9ic2VydmVkLCBQcmVkaWN0aW9uLCBSZWdyZXNzaW9uLAogICAgICAgICAgIGRvdHcpICU+JQogICAgdW5uZXN0KCkgJT4lCiAgZmlsdGVyKFJlZ3Jlc3Npb24gPT0gIkVUaW1lX1NwYWNlX0ZFX3RpbWVMYWdzX2hvbGlkYXlMYWdzIiklPiUKICBtdXRhdGUod2Vla2VuZCA9IGlmZWxzZShkb3R3ICVpbiUgYygiU3VuIiwgIlNhdCIpLCAiV2Vla2VuZCIsICJXZWVrZGF5IiksCiAgICAgICAgIHRpbWVfb2ZfZGF5ID0gY2FzZV93aGVuKGhvdXIoaW50ZXJ2YWw2MCkgPCA3IHwgaG91cihpbnRlcnZhbDYwKSA+IDE4IH4gIk92ZXJuaWdodCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvdXIoaW50ZXJ2YWw2MCkgPj0gNyAmIGhvdXIoaW50ZXJ2YWw2MCkgPCAxMCB+ICJBTSBSdXNoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG91cihpbnRlcnZhbDYwKSA+PSAxMCAmIGhvdXIoaW50ZXJ2YWw2MCkgPCAxNSB+ICJNaWQtRGF5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG91cihpbnRlcnZhbDYwKSA+PSAxNSAmIGhvdXIoaW50ZXJ2YWw2MCkgPD0gMTggfiAiUE0gUnVzaCIpKSAlPiUKICBncm91cF9ieShTdGFydC5TdGF0aW9uLklELCB3ZWVrZW5kLCB0aW1lX29mX2RheSwgZnJvbV9sb25naXR1ZGUsIGZyb21fbGF0aXR1ZGUpICU+JQogIHN1bW1hcml6ZShNQUUgPSBtZWFuKGFicyhPYnNlcnZlZC1QcmVkaWN0aW9uKSwgbmEucm0gPSBUUlVFKSklPiUKICBnZ3Bsb3QoLikrCiAgZ2VvbV9zZihkYXRhID0gY2hhdENlbnN1cywgY29sb3IgPSAiZ3JleSIsIGZpbGwgPSAidHJhbnNwYXJlbnQiKSsKICBnZW9tX3BvaW50KGFlcyh4ID0gZnJvbV9sb25naXR1ZGUsIHkgPSBmcm9tX2xhdGl0dWRlLCBjb2xvciA9IE1BRSksIAogICAgICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIHNpemUgPSAwLjUsIGFscGhhID0gMC44KSsKICBzY2FsZV9jb2xvdXJfdmlyaWRpcyhkaXJlY3Rpb24gPSAtMSwKICBkaXNjcmV0ZSA9IEZBTFNFLCBvcHRpb24gPSAiRCIpKwogIHlsaW0obWluKGRhdF9jZW5zdXMkZnJvbV9sYXRpdHVkZSksIG1heChkYXRfY2Vuc3VzJGZyb21fbGF0aXR1ZGUpKSsKICB4bGltKG1pbihkYXRfY2Vuc3VzJGZyb21fbG9uZ2l0dWRlKSwgbWF4KGRhdF9jZW5zdXMkZnJvbV9sb25naXR1ZGUpKSsKICBmYWNldF9ncmlkKHdlZWtlbmR+dGltZV9vZl9kYXkpKwogIGxhYnModGl0bGU9Ik1lYW4gQWJzb2x1dGUgRXJyb3JzLCBUZXN0IFNldCIpKwogIG1hcFRoZW1lCiAgCmBgYAoKV2hlbiBjb21wYXJpbmcgdGhlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBNZWFuIEFic29sdXRlIEVycm9yIChNQUUpIGFuZCB0aHJlZSBzb2Npby1lY29ub21pYyB2YXJpYWJsZXMgKFBlcmNlbnRfVGFraW5nX1B1YmxpY19UcmFucywgTWVkX0luYywgUGVyY2VudF9XaGl0ZSkgZHVyaW5nIHRoZSAiQU0gUnVzaCIgdmVyc3VzIHRoZSAiUE0gUnVzaCIsIHRoZSAiQU0gUnVzaCIgc3VnZ2VzdHMgYSBzdHJvbmdlciwgbmVnYXRpdmUgcmVsYXRpb25zaGlwIGJld3RlZW4gZXJyb3JzIGFuZCB0aG9zZSB3aG8gdGFrZSBwdWJsaWMgdHJhbnNwb3J0YXRpb24gdG8gd29yay4gCgpgYGB7ciBzdGF0aW9uX3N1bW1hcnkyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlID0gRkFMU0UgfQp3ZWVrX3ByZWRpY3Rpb25zICU+JSAKICAgIG11dGF0ZShpbnRlcnZhbDYwID0gbWFwKGRhdGEsIHB1bGwsIGludGVydmFsNjApLAogICAgICAgICAgIFN0YXJ0LlN0YXRpb24uSUQgPSBtYXAoZGF0YSwgcHVsbCwgU3RhcnQuU3RhdGlvbi5JRCksIAogICAgICAgICAgIGZyb21fbGF0aXR1ZGUgPSBtYXAoZGF0YSwgcHVsbCwgZnJvbV9sYXRpdHVkZSksIAogICAgICAgICAgIGZyb21fbG9uZ2l0dWRlID0gbWFwKGRhdGEsIHB1bGwsIGZyb21fbG9uZ2l0dWRlKSwKICAgICAgICAgICBkb3R3ID0gbWFwKGRhdGEsIHB1bGwsIGRvdHcpLAogICAgICAgICAgIFBlcmNlbnRfVGFraW5nX1B1YmxpY19UcmFucyA9IG1hcChkYXRhLCBwdWxsLCBQZXJjZW50X1Rha2luZ19QdWJsaWNfVHJhbnMpLAogICAgICAgICAgIE1lZF9JbmMgPSBtYXAoZGF0YSwgcHVsbCwgTWVkX0luYyksCiAgICAgICAgICAgUGVyY2VudF9XaGl0ZSA9IG1hcChkYXRhLCBwdWxsLCBQZXJjZW50X1doaXRlKSkgJT4lCiAgICBzZWxlY3QoaW50ZXJ2YWw2MCwgU3RhcnQuU3RhdGlvbi5JRCwgZnJvbV9sb25naXR1ZGUsIAogICAgICAgICAgIGZyb21fbGF0aXR1ZGUsIE9ic2VydmVkLCBQcmVkaWN0aW9uLCBSZWdyZXNzaW9uLAogICAgICAgICAgIGRvdHcsIFBlcmNlbnRfVGFraW5nX1B1YmxpY19UcmFucywgTWVkX0luYywgUGVyY2VudF9XaGl0ZSkgJT4lCiAgICB1bm5lc3QoKSAlPiUKICBmaWx0ZXIoUmVncmVzc2lvbiA9PSAiRVRpbWVfU3BhY2VfRkVfdGltZUxhZ3NfaG9saWRheUxhZ3MiKSU+JQogIG11dGF0ZSh3ZWVrZW5kID0gaWZlbHNlKGRvdHcgJWluJSBjKCJTdW4iLCAiU2F0IiksICJXZWVrZW5kIiwgIldlZWtkYXkiKSwKICAgICAgICAgdGltZV9vZl9kYXkgPSBjYXNlX3doZW4oaG91cihpbnRlcnZhbDYwKSA8IDcgfCBob3VyKGludGVydmFsNjApID4gMTggfiAiT3Zlcm5pZ2h0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG91cihpbnRlcnZhbDYwKSA+PSA3ICYgaG91cihpbnRlcnZhbDYwKSA8IDEwIH4gIkFNIFJ1c2giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBob3VyKGludGVydmFsNjApID49IDEwICYgaG91cihpbnRlcnZhbDYwKSA8IDE1IH4gIk1pZC1EYXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBob3VyKGludGVydmFsNjApID49IDE1ICYgaG91cihpbnRlcnZhbDYwKSA8PSAxOCB+ICJQTSBSdXNoIikpICU+JQogIGZpbHRlcih0aW1lX29mX2RheSA9PSAiQU0gUnVzaCIpICU+JQogIGdyb3VwX2J5KFN0YXJ0LlN0YXRpb24uSUQsIFBlcmNlbnRfVGFraW5nX1B1YmxpY19UcmFucywgTWVkX0luYywgUGVyY2VudF9XaGl0ZSkgJT4lCiAgc3VtbWFyaXplKE1BRSA9IG1lYW4oYWJzKE9ic2VydmVkLVByZWRpY3Rpb24pLCBuYS5ybSA9IFRSVUUpKSU+JQogIGdhdGhlcigtU3RhcnQuU3RhdGlvbi5JRCwgLU1BRSwga2V5ID0gInZhcmlhYmxlIiwgdmFsdWUgPSAidmFsdWUiKSU+JQogIGdncGxvdCguKSsKICAjZ2VvbV9zZihkYXRhID0gY2hhdENlbnN1cywgY29sb3IgPSAiZ3JleSIsIGZpbGwgPSAidHJhbnNwYXJlbnQiKSsKICBnZW9tX3BvaW50KGFlcyh4ID0gdmFsdWUsIHkgPSBNQUUpLCBhbHBoYSA9IDAuNCkrCiAgZ2VvbV9zbW9vdGgoYWVzKHggPSB2YWx1ZSwgeSA9IE1BRSksIG1ldGhvZCA9ICJsbSIsIHNlPSBGQUxTRSkrCiAgZmFjZXRfd3JhcCh+dmFyaWFibGUsIHNjYWxlcyA9ICJmcmVlIikrCiAgbGFicyh0aXRsZT0iRXJyb3JzIGFzIGEgZnVuY3Rpb24gb2Ygc29jaW8tZWNvbm9taWMgdmFyaWFibGVzIC0gQU0gUlVTSCIsCiAgICAgICB5PSJNZWFuIEFic29sdXRlIEVycm9yIChUcmlwcykiKSsKICBwbG90VGhlbWUKCndlZWtfcHJlZGljdGlvbnMgJT4lIAogICAgbXV0YXRlKGludGVydmFsNjAgPSBtYXAoZGF0YSwgcHVsbCwgaW50ZXJ2YWw2MCksCiAgICAgICAgICAgU3RhcnQuU3RhdGlvbi5JRCA9IG1hcChkYXRhLCBwdWxsLCBTdGFydC5TdGF0aW9uLklEKSwgCiAgICAgICAgICAgZnJvbV9sYXRpdHVkZSA9IG1hcChkYXRhLCBwdWxsLCBmcm9tX2xhdGl0dWRlKSwgCiAgICAgICAgICAgZnJvbV9sb25naXR1ZGUgPSBtYXAoZGF0YSwgcHVsbCwgZnJvbV9sb25naXR1ZGUpLAogICAgICAgICAgIGRvdHcgPSBtYXAoZGF0YSwgcHVsbCwgZG90dyksCiAgICAgICAgICAgUGVyY2VudF9UYWtpbmdfUHVibGljX1RyYW5zID0gbWFwKGRhdGEsIHB1bGwsIFBlcmNlbnRfVGFraW5nX1B1YmxpY19UcmFucyksCiAgICAgICAgICAgTWVkX0luYyA9IG1hcChkYXRhLCBwdWxsLCBNZWRfSW5jKSwKICAgICAgICAgICBQZXJjZW50X1doaXRlID0gbWFwKGRhdGEsIHB1bGwsIFBlcmNlbnRfV2hpdGUpKSAlPiUKICAgIHNlbGVjdChpbnRlcnZhbDYwLCBTdGFydC5TdGF0aW9uLklELCBmcm9tX2xvbmdpdHVkZSwgCiAgICAgICAgICAgZnJvbV9sYXRpdHVkZSwgT2JzZXJ2ZWQsIFByZWRpY3Rpb24sIFJlZ3Jlc3Npb24sCiAgICAgICAgICAgZG90dywgUGVyY2VudF9UYWtpbmdfUHVibGljX1RyYW5zLCBNZWRfSW5jLCBQZXJjZW50X1doaXRlKSAlPiUKICAgIHVubmVzdCgpICU+JQogIGZpbHRlcihSZWdyZXNzaW9uID09ICJFVGltZV9TcGFjZV9GRV90aW1lTGFnc19ob2xpZGF5TGFncyIpJT4lCiAgbXV0YXRlKHdlZWtlbmQgPSBpZmVsc2UoZG90dyAlaW4lIGMoIlN1biIsICJTYXQiKSwgIldlZWtlbmQiLCAiV2Vla2RheSIpLAogICAgICAgICB0aW1lX29mX2RheSA9IGNhc2Vfd2hlbihob3VyKGludGVydmFsNjApIDwgNyB8IGhvdXIoaW50ZXJ2YWw2MCkgPiAxOCB+ICJPdmVybmlnaHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBob3VyKGludGVydmFsNjApID49IDcgJiBob3VyKGludGVydmFsNjApIDwgMTAgfiAiQU0gUnVzaCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvdXIoaW50ZXJ2YWw2MCkgPj0gMTAgJiBob3VyKGludGVydmFsNjApIDwgMTUgfiAiTWlkLURheSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvdXIoaW50ZXJ2YWw2MCkgPj0gMTUgJiBob3VyKGludGVydmFsNjApIDw9IDE4IH4gIlBNIFJ1c2giKSkgJT4lCiAgZmlsdGVyKHRpbWVfb2ZfZGF5ID09ICJQTSBSdXNoIikgJT4lCiAgZ3JvdXBfYnkoU3RhcnQuU3RhdGlvbi5JRCwgUGVyY2VudF9UYWtpbmdfUHVibGljX1RyYW5zLCBNZWRfSW5jLCBQZXJjZW50X1doaXRlKSAlPiUKICBzdW1tYXJpemUoTUFFID0gbWVhbihhYnMoT2JzZXJ2ZWQtUHJlZGljdGlvbiksIG5hLnJtID0gVFJVRSkpJT4lCiAgZ2F0aGVyKC1TdGFydC5TdGF0aW9uLklELCAtTUFFLCBrZXkgPSAidmFyaWFibGUiLCB2YWx1ZSA9ICJ2YWx1ZSIpJT4lCiAgZ2dwbG90KC4pKwogICNnZW9tX3NmKGRhdGEgPSBjaGF0Q2Vuc3VzLCBjb2xvciA9ICJncmV5IiwgZmlsbCA9ICJ0cmFuc3BhcmVudCIpKwogIGdlb21fcG9pbnQoYWVzKHggPSB2YWx1ZSwgeSA9IE1BRSksIGFscGhhID0gMC40KSsKICBnZW9tX3Ntb290aChhZXMoeCA9IHZhbHVlLCB5ID0gTUFFKSwgbWV0aG9kID0gImxtIiwgc2U9IEZBTFNFKSsKICBmYWNldF93cmFwKH52YXJpYWJsZSwgc2NhbGVzID0gImZyZWUiKSsKICBsYWJzKHRpdGxlPSJFcnJvcnMgYXMgYSBmdW5jdGlvbiBvZiBzb2Npby1lY29ub21pYyB2YXJpYWJsZXMgLSBQTSBSVVNIIiwKICAgICAgIHk9Ik1lYW4gQWJzb2x1dGUgRXJyb3IgKFRyaXBzKSIpKwogIHBsb3RUaGVtZQogIApgYGAKCiMgKipSZXN1bHRzKiogCgpDcm9zcy12YWxpZGF0aW9uIGlzIHBlcmZvcm1lZCBhY3Jvc3MgdGltZSwgcmF0aGVyIHRoYW4gc3BhY2UsIHRvIGFzc2VzcyB0aGUgcGVyZm9ybWFuY2UgYW5kIGdlbmVyYWxpemFiaWxpdHkgb2YgYSBwcmVkaWN0aXZlIG1vZGVsLiBUaGUgcmVzdWx0cyBvZiB0aGUgbW9kZWwgaW5kaWNhdGUgYSBNZWFuIEFic29sdXRlIEVycm9yIChNQUUpIG9mIGFwcHJveGltYXRlbHkgMC42NDYgd2l0aCBhIHN0YW5kYXJkIGRldmlhdGlvbiBvZiBhcm91bmQgMC4wMzcuIEEgbG93ZXIgTUFFIGlzIGdlbmVyYWxseSBkZXNpcmFibGUsIGluZGljYXRpbmcgYmV0dGVyIHByZWRpY3RpdmUgcGVyZm9ybWFuY2UuIFRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIE1BRSBnaXZlcyBhbiBpZGVhIG9mIHRoZSB2YXJpYWJpbGl0eSBvZiBwcmVkaWN0aW9uIGVycm9ycyBhY3Jvc3MgZGlmZmVyZW50IGZvbGRzIG9mIHRoZSBjcm9zcy12YWxpZGF0aW9uIHByb2Nlc3MuCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpjb250cm9sIDwtIHRyYWluQ29udHJvbChtZXRob2Q9ImN2IiwgbnVtYmVyPTEwMCkKCnNldC5zZWVkKDQyKQoKbW9kZWxfY3YgPC0gdHJhaW4oVHJpcF9Db3VudCB+IFN0YXJ0LlN0YXRpb24uSUQgKyBob3VyKGludGVydmFsNjApICsgZG90dyArIFRlbXBlcmF0dXJlICsgUHJlY2lwaXRhdGlvbiArCiAgICAgICAgICAgICAgICAgICBsYWdIb3VyICsgbGFnMkhvdXJzICtsYWczSG91cnMgK2xhZzEySG91cnMgKyBsYWcxZGF5ICsgaG9saWRheUxhZyArIGhvbGlkYXkgKyAKICAgICAgICAgICAgICAgICAgIFRvdGFsX1BvcCArIE1lZF9JbmMgKyBQZXJjZW50X1Rha2luZ19QdWJsaWNfVHJhbnMsIAogICAgICAgICAgICAgICAgICBkYXRhPW5hLm9taXQocmlkZS5wYW5lbCksCiAgICAgICAgICAgICAgICAgIG1ldGhvZD0ibG0iLAogICAgICAgICAgICAgICAgICB0ckNvbnRyb2w9Y29udHJvbCkKCm1vZGVsX2N2JHJlc2FtcGxlICU+JSAKICBzdW1tYXJpc2UoTUFFID0gbWVhbihtb2RlbF9jdiRyZXNhbXBsZVssM10pLAogICAgICAgICAgICBzZChtb2RlbF9jdiRyZXNhbXBsZVssM10pCikgJT4lCiAga2JsKGNvbC5uYW1lPWMoJ01lYW4gQWJzb2x1dGUgRXJyb3InLCdTdGFuZGFyZCBEZXZpYXRpb24gb2YgTUFFJykpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpKQpgYGAKCgpFbmhhbmNpbmcgdGhlIG1vZGVsJ3MgcGVyZm9ybWFuY2UgcmVsaWVzIG9uIHR3byBtYWluIGZhY3RvcnM6IHJlZmluaW5nIGZlYXR1cmUgZW5naW5lZXJpbmcgYW5kIGluY3JlYXNpbmcgY29tcHV0YXRpb25hbCByZXNvdXJjZXMuIEluY29ycG9yYXRpbmcgYmluYXJ5IHZhcmlhYmxlcyB0byBhY2NvdW50IGZvciBmYWN0b3JzIGxpa2UgcnVzaCBob3VycyBtYXkgaW1wcm92ZSBhY2N1cmFjeS4gSG93ZXZlciwgdGhlIG1vc3Qgc2lnbmlmaWNhbnQgaW1wcm92ZW1lbnQgd291bGQgbGlrZWx5IGNvbWUgZnJvbSBleHBhbmRpbmcgdGhlIGRhdGFzZXQsIGVuYWJsaW5nIHRoZSBtb2RlbCB0byBkaXNjZXJuIG51YW5jZWQgcGF0dGVybnMsIHN1Y2ggYXMgdmFyaWF0aW9ucyBpbiBjb21tdXRpbmcgYmVoYXZpb3Igb24gZGlmZmVyZW50IGhvbGlkYXlzLCBzdWNoIGFzIE1hcmF0aG9uIFN1bmRheS5BZGRpdGlvbmFsbHksIHRyYW5zaXRpb25pbmcgZnJvbSBvcmRpbmFyeSBsZWFzdCBzcXVhcmVzIChPTFMpIHJlZ3Jlc3Npb24gdG8gbW9kZWxzIGV4cGxpY2l0bHkgYWRkcmVzc2luZyBzcGF0aWFsIGRlcGVuZGVuY2llcywgc3VjaCBhcyBzcGF0aWFsIGxhZyByZWdyZXNzaW9uIG9yIGdlb2dyYXBoaWNhbGx5LXdlaWdodGVkIHJlZ3Jlc3Npb24sIHBhcnRpY3VsYXJseSBhcm91bmQgdGhlIGNpdHkncyBoaWdod2F5cyBhbmQgd2F0ZXJ3YXlzLiBVbHRpbWF0ZWx5LCBtYXhpbWl6aW5nIHRoZSBkYXRhc2V0IHNpemUgd291bGQgbGlrZWx5IHByb3ZlIGJlbmVmaWNpYWwuIFRyYWluaW5nIHRoZSBtb2RlbCBvbiBhIG1vcmUgZXh0ZW5zaXZlIGhpc3RvcmljYWwgZGF0YXNldCwgZW5jb21wYXNzaW5nIHRoZSBhcHByb3hpbWF0ZWx5IDExIHllYXJzIG9mIGF2YWlsYWJsZSBCaWtlIENoYXR0YW5vb2dhIGRhdGEsIHdvdWxkIGxpa2VseSB5aWVsZCBzdWJzdGFudGlhbCBpbXByb3ZlbWVudHMgaW4gYWNjdXJhY3kgYW5kIGdlbmVyYWxpemFiaWxpdHkuIAoKIyAqKkNvbmNsdXNpb24qKiAKCk91ciBtb2RlbCBvZmZlcnMgdmFsdWFibGUgaW5zaWdodHMgZm9yIG9wdGltaXppbmcgQmlrZS1DaGF0dGFub29nYSdzIHJlYmFsYW5jaW5nIGVmZm9ydHMuIEl0cyBvdmVyYWxsIGFjY3VyYWN5IHN1cnBhc3NlcyBhIG5haXZlIGFwcHJvYWNoIHRoYXQgcmVhY3RzIHRvIGVtZXJnaW5nIGRlbWFuZC4gSG93ZXZlciwgY2hhbGxlbmdlcyBwZXJzaXN0LCBwYXJ0aWN1bGFybHkgaW4gaGFuZGxpbmcgdGhlIG5vbi1yYW5kb20gc3BhdGlhbCBkaXN0cmlidXRpb24gb2YgZXJyb3JzIGFyb3VuZCB0aGUgVGVubmVzc2VlIFJpdmVyLiBOb3RhYmx5LCB0aGUgbW9kZWwgc3RydWdnbGVzIHRvIGdlbmVyYWxpemUgd2VsbCBpbiBoaWdoLWRlbWFuZCBhcmVhcywgd2hlcmUgdGhlIGNvbnNlcXVlbmNlcyBvZiB1bmRlcnByZWRpY3Rpb24gYXJlIG1vc3QgcHJvbm91bmNlZC4gV2hpbGUgdGhlIG1vZGVsIHJlbWFpbnMgYSByb2J1c3QgcHJlZGljdGl2ZSB0b29sLCB0aGVyZSBhcmUgbm90YWJsZSBvcHBvcnR1bml0aWVzIGZvciBpbXByb3ZlbWVudCwgZXNwZWNpYWxseSBpbiBlbmhhbmNpbmcgaXRzIHRyZWF0bWVudCBvZiBzcGF0aWFsIGZpeGVkIGVmZmVjdHMK